from os.path import dirname, abspath
-class Config():
+class Config:
def __init__(self, fname):
"""
Basic configuration file object
@staticmethod
def _load(fname: str) -> str:
- with open(fname, 'rt', encoding='utf-8') as fd:
+ with open(fname, "rt", encoding="utf-8") as fd:
data = fd.read()
return data.rstrip()
def __str__(self):
- """ Return a JSON representation of the config """
+ """Return a JSON representation of the config"""
return json.dumps(self.config, indent=4)
@abstractmethod
Parse a .config file, individual config options can be accessed via
.config[<CONFIG_OPTION>]
"""
+
def _parse(self, data: str):
self.config = {}
for line in data.splitlines():
- m = re.match(r'^# (CONFIG_.*) is not set$', line)
+ m = re.match(r"^# (CONFIG_.*) is not set$", line)
if m:
self.config[m.group(1)] = literal_eval("'n'")
continue
- m = re.match(r'^(CONFIG_[A-Za-z0-9_]+)=(.*)$', line)
+ m = re.match(r"^(CONFIG_[A-Za-z0-9_]+)=(.*)$", line)
if m:
self.config[m.group(1)] = literal_eval("'" + m.group(2) + "'")
continue
"""
Parse body of annotations file
"""
+
def _parse_body(self, data: str, parent=True):
for line in data.splitlines():
# Replace tabs with spaces, squeeze multiple into singles and
# remove leading and trailing spaces
- line = line.replace('\t', ' ')
- line = re.sub(r' +', ' ', line)
+ line = line.replace("\t", " ")
+ line = re.sub(r" +", " ", line)
line = line.strip()
# Ignore empty lines
continue
# Catpure flavors of included files
- if line.startswith('# FLAVOUR: '):
- self.include_flavour += line.split(' ')[2:]
+ if line.startswith("# FLAVOUR: "):
+ self.include_flavour += line.split(" ")[2:]
continue
# Ignore comments
- if line.startswith('#'):
+ if line.startswith("#"):
continue
# Handle includes (recursively)
if m:
if parent:
self.include.append(m.group(1))
- include_fname = dirname(abspath(self.fname)) + '/' + m.group(1)
+ include_fname = dirname(abspath(self.fname)) + "/" + m.group(1)
include_data = self._load(include_fname)
self._parse_body(include_data, parent=False)
continue
# Handle policy and note lines
- if re.match(r'.* (policy|note)<', line):
+ if re.match(r".* (policy|note)<", line):
try:
- conf = line.split(' ')[0]
+ conf = line.split(" ")[0]
if conf in self.config:
entry = self.config[conf]
else:
- entry = {'policy': {}}
+ entry = {"policy": {}}
match = False
- m = re.match(r'.* policy<(.*?)>', line)
+ m = re.match(r".* policy<(.*?)>", line)
if m:
match = True
try:
- entry['policy'] |= literal_eval(m.group(1))
+ entry["policy"] |= literal_eval(m.group(1))
except TypeError:
- entry['policy'] = {**entry['policy'], **literal_eval(m.group(1))}
+ entry["policy"] = {
+ **entry["policy"],
+ **literal_eval(m.group(1)),
+ }
- m = re.match(r'.* note<(.*?)>', line)
+ m = re.match(r".* note<(.*?)>", line)
if m:
- entry['oneline'] = match
+ entry["oneline"] = match
match = True
- entry['note'] = "'" + m.group(1).replace("'", '') + "'"
+ entry["note"] = "'" + m.group(1).replace("'", "") + "'"
if not match:
- raise SyntaxError('syntax error')
+ raise SyntaxError("syntax error")
self.config[conf] = entry
except Exception as e:
- raise SyntaxError(str(e) + f', line = {line}') from e
+ raise SyntaxError(str(e) + f", line = {line}") from e
continue
# Invalid line
- raise SyntaxError(f'invalid line: {line}')
+ raise SyntaxError(f"invalid line: {line}")
def _parse(self, data: str):
"""
self.flavour = []
self.flavour_dep = {}
self.include = []
- self.header = ''
+ self.header = ""
self.include_flavour = []
# Parse header (only main header will considered, headers in includes
# will be treated as comments)
for line in data.splitlines():
- if re.match(r'^#.*', line):
- m = re.match(r'^# ARCH: (.*)', line)
+ if re.match(r"^#.*", line):
+ m = re.match(r"^# ARCH: (.*)", line)
if m:
- self.arch = list(m.group(1).split(' '))
- m = re.match(r'^# FLAVOUR: (.*)', line)
+ self.arch = list(m.group(1).split(" "))
+ m = re.match(r"^# FLAVOUR: (.*)", line)
if m:
- self.flavour = list(m.group(1).split(' '))
- m = re.match(r'^# FLAVOUR_DEP: (.*)', line)
+ self.flavour = list(m.group(1).split(" "))
+ m = re.match(r"^# FLAVOUR_DEP: (.*)", line)
if m:
self.flavour_dep = literal_eval(m.group(1))
self.header += line + "\n"
# Sanity check: Verify that all FLAVOUR_DEP flavors are valid
for src, tgt in self.flavour_dep.items():
if src not in self.flavour:
- raise SyntaxError(f'Invalid source flavour in FLAVOUR_DEP: {src}')
+ raise SyntaxError(f"Invalid source flavour in FLAVOUR_DEP: {src}")
if tgt not in self.include_flavour:
- raise SyntaxError(f'Invalid target flavour in FLAVOUR_DEP: {tgt}')
+ raise SyntaxError(f"Invalid target flavour in FLAVOUR_DEP: {tgt}")
def _remove_entry(self, config: str):
if self.config[config]:
return
if arch is not None:
if flavour is not None:
- flavour = f'{arch}-{flavour}'
+ flavour = f"{arch}-{flavour}"
else:
flavour = arch
- del self.config[config]['policy'][flavour]
- if not self.config[config]['policy']:
+ del self.config[config]["policy"][flavour]
+ if not self.config[config]["policy"]:
self._remove_entry(config)
else:
self._remove_entry(config)
- def set(self, config: str, arch: str = None, flavour: str = None,
- value: str = None, note: str = None):
+ def set(
+ self,
+ config: str,
+ arch: str = None,
+ flavour: str = None,
+ value: str = None,
+ note: str = None,
+ ):
if value is not None:
if config not in self.config:
- self.config[config] = {'policy': {}}
+ self.config[config] = {"policy": {}}
if arch is not None:
if flavour is not None:
- flavour = f'{arch}-{flavour}'
+ flavour = f"{arch}-{flavour}"
else:
flavour = arch
- self.config[config]['policy'][flavour] = value
+ self.config[config]["policy"][flavour] = value
else:
for a in self.arch:
- self.config[config]['policy'][a] = value
+ self.config[config]["policy"][a] = value
if note is not None:
- self.config[config]['note'] = "'" + note.replace("'", '') + "'"
+ self.config[config]["note"] = "'" + note.replace("'", "") + "'"
def update(self, c: KConfig, arch: str, flavour: str = None, configs: list = None):
- """ Merge configs from a Kconfig object into Annotation object """
+ """Merge configs from a Kconfig object into Annotation object"""
# Determine if we need to import all configs or a single config
if not configs:
try:
configs |= self.search_config(arch=arch, flavour=flavour).keys()
except TypeError:
- configs = {**configs, **self.search_config(arch=arch, flavour=flavour).keys()}
+ configs = {
+ **configs,
+ **self.search_config(arch=arch, flavour=flavour).keys(),
+ }
# Import configs from the Kconfig object into Annotations
if flavour is not None:
- flavour = arch + f'-{flavour}'
+ flavour = arch + f"-{flavour}"
else:
flavour = arch
for conf in configs:
if conf in c.config:
val = c.config[conf]
else:
- val = '-'
+ val = "-"
if conf in self.config:
- if 'policy' in self.config[conf]:
- self.config[conf]['policy'][flavour] = val
+ if "policy" in self.config[conf]:
+ self.config[conf]["policy"][flavour] = val
else:
- self.config[conf]['policy'] = {flavour: val}
+ self.config[conf]["policy"] = {flavour: val}
else:
- self.config[conf] = {'policy': {flavour: val}}
+ self.config[conf] = {"policy": {flavour: val}}
def _compact(self):
# Try to remove redundant settings: if the config value of a flavour is
# the same as the one of the main arch simply drop it.
for conf in self.config.copy():
- if 'policy' not in self.config[conf]:
+ if "policy" not in self.config[conf]:
continue
for flavour in self.flavour:
- if flavour not in self.config[conf]['policy']:
+ if flavour not in self.config[conf]["policy"]:
continue
- m = re.match(r'^(.*?)-(.*)$', flavour)
+ m = re.match(r"^(.*?)-(.*)$", flavour)
if not m:
continue
arch = m.group(1)
- if arch in self.config[conf]['policy']:
- if self.config[conf]['policy'][flavour] == self.config[conf]['policy'][arch]:
- del self.config[conf]['policy'][flavour]
+ if arch in self.config[conf]["policy"]:
+ if (
+ self.config[conf]["policy"][flavour]
+ == self.config[conf]["policy"][arch]
+ ):
+ del self.config[conf]["policy"][flavour]
continue
if flavour not in self.flavour_dep:
continue
generic = self.flavour_dep[flavour]
- if generic in self.config[conf]['policy']:
- if self.config[conf]['policy'][flavour] == self.config[conf]['policy'][generic]:
- del self.config[conf]['policy'][flavour]
+ if generic in self.config[conf]["policy"]:
+ if (
+ self.config[conf]["policy"][flavour]
+ == self.config[conf]["policy"][generic]
+ ):
+ del self.config[conf]["policy"][flavour]
continue
# Remove rules for flavours / arches that are not supported (not
# listed in the annotations header).
- for flavour in self.config[conf]['policy'].copy():
+ for flavour in self.config[conf]["policy"].copy():
if flavour not in list(set(self.arch + self.flavour)):
- del self.config[conf]['policy'][flavour]
+ del self.config[conf]["policy"][flavour]
# Remove configs that are all undefined across all arches/flavours
# (unless we have includes)
if not self.include:
- if 'policy' in self.config[conf]:
- if list(set(self.config[conf]['policy'].values())) == ['-']:
- self.config[conf]['policy'] = {}
+ if "policy" in self.config[conf]:
+ if list(set(self.config[conf]["policy"].values())) == ["-"]:
+ self.config[conf]["policy"] = {}
# Drop empty rules
- if not self.config[conf]['policy']:
+ if not self.config[conf]["policy"]:
del self.config[conf]
else:
# Compact same value across all flavour within the same arch
arch_flavours = [i for i in self.flavour if i.startswith(arch)]
value = None
for flavour in arch_flavours:
- if flavour not in self.config[conf]['policy']:
+ if flavour not in self.config[conf]["policy"]:
break
if value is None:
- value = self.config[conf]['policy'][flavour]
- elif value != self.config[conf]['policy'][flavour]:
+ value = self.config[conf]["policy"][flavour]
+ elif value != self.config[conf]["policy"][flavour]:
break
else:
for flavour in arch_flavours:
- del self.config[conf]['policy'][flavour]
- self.config[conf]['policy'][arch] = value
+ del self.config[conf]["policy"][flavour]
+ self.config[conf]["policy"][arch] = value
# After the first round of compaction we may end up having configs that
# are undefined across all arches, so do another round of compaction to
# drop these settings that are not needed anymore
if not self.include:
for conf in self.config.copy():
# Remove configs that are all undefined across all arches/flavours
- if 'policy' in self.config[conf]:
- if list(set(self.config[conf]['policy'].values())) == ['-']:
- self.config[conf]['policy'] = {}
+ if "policy" in self.config[conf]:
+ if list(set(self.config[conf]["policy"].values())) == ["-"]:
+ self.config[conf]["policy"] = {}
# Drop empty rules
- if not self.config[conf]['policy']:
+ if not self.config[conf]["policy"]:
del self.config[conf]
@staticmethod
def _sorted(config):
- """ Sort configs alphabetically but return configs with a note first """
+ """Sort configs alphabetically but return configs with a note first"""
w_note = []
wo_note = []
for c in sorted(config):
- if 'note' in config[c]:
+ if "note" in config[c]:
w_note.append(c)
else:
wo_note.append(c)
return w_note + wo_note
def save(self, fname: str):
- """ Save annotations data to the annotation file """
+ """Save annotations data to the annotation file"""
# Compact annotations structure
self._compact()
# Save annotations to disk
- with tempfile.NamedTemporaryFile(mode='w+t', delete=False) as tmp:
+ with tempfile.NamedTemporaryFile(mode="w+t", delete=False) as tmp:
# Write header
- tmp.write(self.header + '\n')
+ tmp.write(self.header + "\n")
# Write includes
for i in self.include:
marker = False
for conf in self._sorted(self.config):
new_val = self.config[conf]
- if 'policy' not in new_val:
+ if "policy" not in new_val:
continue
# If new_val is a subset of old_val, skip it unless there are
# new notes that are different than the old ones.
old_val = tmp_a.config.get(conf)
- if old_val and 'policy' in old_val:
+ if old_val and "policy" in old_val:
try:
- can_skip = old_val['policy'] == old_val['policy'] | new_val['policy']
+ can_skip = (
+ old_val["policy"] == old_val["policy"] | new_val["policy"]
+ )
except TypeError:
- can_skip = old_val['policy'] == {**old_val['policy'], **new_val['policy']}
+ can_skip = old_val["policy"] == {
+ **old_val["policy"],
+ **new_val["policy"],
+ }
if can_skip:
- if 'note' not in new_val:
+ if "note" not in new_val:
continue
- if 'note' in old_val and 'note' in new_val:
- if old_val['note'] == new_val['note']:
+ if "note" in old_val and "note" in new_val:
+ if old_val["note"] == new_val["note"]:
continue
# Write out the policy (and note) line(s)
- val = dict(sorted(new_val['policy'].items()))
+ val = dict(sorted(new_val["policy"].items()))
line = f"{conf : <47} policy<{val}>"
- if 'note' in new_val:
- val = new_val['note']
- if new_val.get('oneline', False):
+ if "note" in new_val:
+ val = new_val["note"]
+ if new_val.get("oneline", False):
# Single line
- line += f' note<{val}>'
+ line += f" note<{val}>"
else:
# Separate policy and note lines,
# followed by an empty line
- line += f'\n{conf : <47} note<{val}>\n'
+ line += f"\n{conf : <47} note<{val}>\n"
elif not marker:
# Write out a marker indicating the start of annotations
# without notes
- tmp.write('\n# ---- Annotations without notes ----\n\n')
+ tmp.write("\n# ---- Annotations without notes ----\n\n")
marker = True
tmp.write(line + "\n")
tmp.flush()
shutil.move(tmp.name, fname)
- def search_config(self, config: str = None, arch: str = None, flavour: str = None) -> dict:
- """ Return config value of a specific config option or architecture """
+ def search_config(
+ self, config: str = None, arch: str = None, flavour: str = None
+ ) -> dict:
+ """Return config value of a specific config option or architecture"""
if flavour is None:
- flavour = 'generic'
- flavour = f'{arch}-{flavour}'
+ flavour = "generic"
+ flavour = f"{arch}-{flavour}"
if flavour in self.flavour_dep:
generic = self.flavour_dep[flavour]
else:
# Get config options of a specific architecture
ret = {}
for c, val in self.config.items():
- if 'policy' not in val:
+ if "policy" not in val:
continue
- if flavour in val['policy']:
- ret[c] = val['policy'][flavour]
- elif generic != flavour and generic in val['policy']:
- ret[c] = val['policy'][generic]
- elif arch in val['policy']:
- ret[c] = val['policy'][arch]
+ if flavour in val["policy"]:
+ ret[c] = val["policy"][flavour]
+ elif generic != flavour and generic in val["policy"]:
+ ret[c] = val["policy"][generic]
+ elif arch in val["policy"]:
+ ret[c] = val["policy"][arch]
return ret
if config is not None and arch is None:
# Get a specific config option for all architectures
if config is not None and arch is not None:
# Get a specific config option for a specific architecture
if config in self.config:
- if 'policy' in self.config[config]:
- if flavour in self.config[config]['policy']:
- return {config: self.config[config]['policy'][flavour]}
- if generic != flavour and generic in self.config[config]['policy']:
- return {config: self.config[config]['policy'][generic]}
- if arch in self.config[config]['policy']:
- return {config: self.config[config]['policy'][arch]}
+ if "policy" in self.config[config]:
+ if flavour in self.config[config]["policy"]:
+ return {config: self.config[config]["policy"][flavour]}
+ if generic != flavour and generic in self.config[config]["policy"]:
+ return {config: self.config[config]["policy"][generic]}
+ if arch in self.config[config]["policy"]:
+ return {config: self.config[config]["policy"][arch]}
return None
@staticmethod
def to_config(data: dict) -> str:
- """ Convert annotations data to .config format """
- s = ''
+ """Convert annotations data to .config format"""
+ s = ""
for c in data:
v = data[c]
- if v == 'n':
+ if v == "n":
s += f"# {c} is not set\n"
- elif v == '-':
+ elif v == "-":
pass
else:
s += f"{c}={v}\n"
# Copyright © 2022 Canonical Ltd.
import sys
-sys.dont_write_bytecode = True
import os
import argparse
import json
from kconfig.annotations import Annotation, KConfig
-VERSION = '0.1'
+VERSION = "0.1"
SKIP_CONFIGS = (
# CONFIG_VERSION_SIGNATURE is dynamically set during the build
- 'CONFIG_VERSION_SIGNATURE',
+ "CONFIG_VERSION_SIGNATURE",
# Allow to use a different versions of toolchain tools
- 'CONFIG_GCC_VERSION',
- 'CONFIG_CC_VERSION_TEXT',
- 'CONFIG_AS_VERSION',
- 'CONFIG_LD_VERSION',
- 'CONFIG_LLD_VERSION',
- 'CONFIG_CLANG_VERSION',
- 'CONFIG_PAHOLE_VERSION',
- 'CONFIG_RUSTC_VERSION_TEXT',
- 'CONFIG_BINDGEN_VERSION_TEXT',
+ "CONFIG_GCC_VERSION",
+ "CONFIG_CC_VERSION_TEXT",
+ "CONFIG_AS_VERSION",
+ "CONFIG_LD_VERSION",
+ "CONFIG_LLD_VERSION",
+ "CONFIG_CLANG_VERSION",
+ "CONFIG_PAHOLE_VERSION",
+ "CONFIG_RUSTC_VERSION_TEXT",
+ "CONFIG_BINDGEN_VERSION_TEXT",
)
def make_parser():
parser = argparse.ArgumentParser(
- description='Manage Ubuntu kernel .config and annotations',
+ description="Manage Ubuntu kernel .config and annotations",
+ )
+ parser.add_argument(
+ "--version", "-v", action="version", version=f"%(prog)s {VERSION}"
+ )
+
+ parser.add_argument(
+ "--file",
+ "-f",
+ action="store",
+ help="Pass annotations or .config file to be parsed",
+ )
+ parser.add_argument("--arch", "-a", action="store", help="Select architecture")
+ parser.add_argument(
+ "--flavour", "-l", action="store", help='Select flavour (default is "generic")'
+ )
+ parser.add_argument(
+ "--config", "-c", action="store", help="Select a specific config option"
+ )
+ parser.add_argument("--query", "-q", action="store_true", help="Query annotations")
+ parser.add_argument(
+ "--note",
+ "-n",
+ action="store",
+ help="Write a specific note to a config option in annotations",
+ )
+ parser.add_argument(
+ "--autocomplete",
+ action="store_true",
+ help="Enable config bash autocomplete: `source <(annotations --autocomplete)`",
+ )
+ parser.add_argument(
+ "--source",
+ "-t",
+ action="store_true",
+ help="Jump to a config definition in the kernel source code",
+ )
+
+ ga = parser.add_argument_group(title="Action").add_mutually_exclusive_group(
+ required=False
+ )
+ ga.add_argument(
+ "--write",
+ "-w",
+ action="store",
+ metavar="VALUE",
+ dest="value",
+ help="Set a specific config value in annotations (use 'null' to remove)",
+ )
+ ga.add_argument(
+ "--export",
+ "-e",
+ action="store_true",
+ help="Convert annotations to .config format",
+ )
+ ga.add_argument(
+ "--import",
+ "-i",
+ action="store",
+ metavar="FILE",
+ dest="import_file",
+ help="Import a full .config for a specific arch and flavour into annotations",
+ )
+ ga.add_argument(
+ "--update",
+ "-u",
+ action="store",
+ metavar="FILE",
+ dest="update_file",
+ help="Import a partial .config into annotations (only resync configs specified in FILE)",
+ )
+ ga.add_argument(
+ "--check",
+ "-k",
+ action="store",
+ metavar="FILE",
+ dest="check_file",
+ help="Validate kernel .config with annotations",
)
- parser.add_argument('--version', '-v', action='version', version=f'%(prog)s {VERSION}')
-
- parser.add_argument('--file', '-f', action='store',
- help='Pass annotations or .config file to be parsed')
- parser.add_argument('--arch', '-a', action='store',
- help='Select architecture')
- parser.add_argument('--flavour', '-l', action='store',
- help='Select flavour (default is "generic")')
- parser.add_argument('--config', '-c', action='store',
- help='Select a specific config option')
- parser.add_argument('--query', '-q', action='store_true',
- help='Query annotations')
- parser.add_argument('--note', '-n', action='store',
- help='Write a specific note to a config option in annotations')
- parser.add_argument('--autocomplete', action='store_true',
- help='Enable config bash autocomplete: `source <(annotations --autocomplete)`')
- parser.add_argument('--source', '-t', action='store_true',
- help='Jump to a config definition in the kernel source code')
-
- ga = parser.add_argument_group(title='Action').add_mutually_exclusive_group(required=False)
- ga.add_argument('--write', '-w', action='store',
- metavar='VALUE', dest='value',
- help='Set a specific config value in annotations (use \'null\' to remove)')
- ga.add_argument('--export', '-e', action='store_true',
- help='Convert annotations to .config format')
- ga.add_argument('--import', '-i', action='store',
- metavar="FILE", dest='import_file',
- help='Import a full .config for a specific arch and flavour into annotations')
- ga.add_argument('--update', '-u', action='store',
- metavar="FILE", dest='update_file',
- help='Import a partial .config into annotations (only resync configs specified in FILE)')
- ga.add_argument('--check', '-k', action='store',
- metavar="FILE", dest='check_file',
- help='Validate kernel .config with annotations')
return parser
_ARGPARSER = make_parser()
-def arg_fail(message):
+def arg_fail(message, show_usage=True):
print(message)
- _ARGPARSER.print_usage()
+ if show_usage:
+ _ARGPARSER.print_usage()
sys.exit(1)
def print_result(config, res):
if res is not None and config not in res:
- res = {config or '*': res}
+ res = {config or "*": res}
print(json.dumps(res, indent=4))
def do_query(args):
if args.arch is None and args.flavour is not None:
- arg_fail('error: --flavour requires --arch')
+ arg_fail("error: --flavour requires --arch")
a = Annotation(args.file)
res = a.search_config(config=args.config, arch=args.arch, flavour=args.flavour)
print_result(args.config, res)
def do_autocomplete(args):
a = Annotation(args.file)
- res = (c.removeprefix('CONFIG_') for c in a.search_config())
- res_str = ' '.join(res)
+ res = (c.removeprefix("CONFIG_") for c in a.search_config())
+ res_str = " ".join(res)
print(f'complete -W "{res_str}" annotations')
def do_source(args):
if args.config is None:
- arg_fail('error: --source requires --config')
- if not os.path.exists('tags'):
- print('tags not found in the current directory, try: `make tags`')
+ arg_fail("error: --source requires --config")
+ if not os.path.exists("tags"):
+ print("tags not found in the current directory, try: `make tags`")
sys.exit(1)
- os.system(f'vim -t {args.config}')
+ os.system(f"vim -t {args.config}")
def do_note(args):
if args.config is None:
- arg_fail('error: --note requires --config')
+ arg_fail("error: --note requires --config")
# Set the note in annotations
a = Annotation(args.file)
def do_write(args):
if args.config is None:
- arg_fail('error: --write requires --config')
+ arg_fail("error: --write requires --config")
# Set the value in annotations ('null' means remove)
a = Annotation(args.file)
- if args.value == 'null':
+ if args.value == "null":
a.remove(args.config, arch=args.arch, flavour=args.flavour)
else:
- a.set(args.config, arch=args.arch, flavour=args.flavour, value=args.value, note=args.note)
+ a.set(
+ args.config,
+ arch=args.arch,
+ flavour=args.flavour,
+ value=args.value,
+ note=args.note,
+ )
# Save back to annotations
a.save(args.file)
def do_export(args):
if args.arch is None:
- arg_fail('error: --export requires --arch')
+ arg_fail("error: --export requires --arch")
a = Annotation(args.file)
conf = a.search_config(config=args.config, arch=args.arch, flavour=args.flavour)
if conf:
def do_import(args):
if args.arch is None:
- arg_fail('error: --arch is required with --import')
+ arg_fail("error: --arch is required with --import")
if args.flavour is None:
- arg_fail('error: --flavour is required with --import')
+ arg_fail("error: --flavour is required with --import")
if args.config is not None:
- arg_fail('error: --config cannot be used with --import (try --update)')
+ arg_fail("error: --config cannot be used with --import (try --update)")
# Merge with the current annotations
a = Annotation(args.file)
def do_update(args):
if args.arch is None:
- arg_fail('error: --arch is required with --update')
+ arg_fail("error: --arch is required with --update")
# Merge with the current annotations
a = Annotation(args.file)
def do_check(args):
# Determine arch and flavour
if args.arch is None:
- arg_fail('error: --arch is required with --check')
+ arg_fail("error: --arch is required with --check")
print(f"check-config: loading annotations from {args.file}")
total = good = ret = 0
if conf in SKIP_CONFIGS:
continue
entry = a.search_config(config=conf, arch=args.arch, flavour=args.flavour)
- expected = entry[conf] if entry else '-'
- value = c.config[conf] if conf in c.config else '-'
+ expected = entry[conf] if entry else "-"
+ value = c.config[conf] if conf in c.config else "-"
if value != expected:
- policy = a.config[conf] if conf in a.config else 'undefined'
- if 'policy' in policy:
+ policy = a.config[conf] if conf in a.config else "undefined"
+ if "policy" in policy:
policy = f"policy<{policy['policy']}>"
print(f"check-config: FAIL: ({value} != {expected}): {conf} {policy})")
ret = 1
# If --file/-f isn't specified try to automatically determine the right
# location of the annotations file looking at debian/debian.env.
try:
- with open('debian/debian.env', 'rt', encoding='utf-8') as fd:
- args.file = fd.read().rstrip().split('=')[1] + '/config/annotations'
+ with open("debian/debian.env", "rt", encoding="utf-8") as fd:
+ args.file = fd.read().rstrip().split("=")[1] + "/config/annotations"
except (FileNotFoundError, IndexError):
- arg_fail('error: could not determine DEBDIR, try using: --file/-f')
+ arg_fail(
+ "error: could not determine DEBDIR, try using: --file/-f", show_usage=False
+ )
def main():
args = _ARGPARSER.parse_args()
autodetect_annotations(args)
- if args.config and not args.config.startswith('CONFIG_'):
- args.config = 'CONFIG_' + args.config
+ if args.config and not args.config.startswith("CONFIG_"):
+ args.config = "CONFIG_" + args.config
if args.value:
do_write(args)
do_query(args)
-if __name__ == '__main__':
+if __name__ == "__main__":
main()