| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- # SPDX-License-Identifier: GPL-2.0+
- # Copyright 2022 Google LLC
- # Written by Simon Glass <sjg@chromium.org>
- #
- """Utility functions for dealing with Kconfig .confing files"""
- import re
- from u_boot_pylib import tools
- RE_LINE = re.compile(r'(# )?CONFIG_([A-Z0-9_]+)(=(.*)| is not set)')
- RE_CFG = re.compile(r'(~?)(CONFIG_)?([A-Z0-9_]+)(=.*)?')
- def make_cfg_line(opt, adj):
- """Make a new config line for an option
- Args:
- opt (str): Option to process, without CONFIG_ prefix
- adj (str): Adjustment to make (C is config option without prefix):
- C to enable C
- ~C to disable C
- C=val to set the value of C (val must have quotes if C is
- a string Kconfig)
- Returns:
- str: New line to use, one of:
- CONFIG_opt=y - option is enabled
- # CONFIG_opt is not set - option is disabled
- CONFIG_opt=val - option is getting a new value (val is
- in quotes if this is a string)
- """
- if adj[0] == '~':
- return f'# CONFIG_{opt} is not set'
- if '=' in adj:
- return f'CONFIG_{adj}'
- return f'CONFIG_{opt}=y'
- def adjust_cfg_line(line, adjust_cfg, done=None):
- """Make an adjustment to a single of line from a .config file
- This processes a .config line, producing a new line if a change for this
- CONFIG is requested in adjust_cfg
- Args:
- line (str): line to process, e.g. '# CONFIG_FRED is not set' or
- 'CONFIG_FRED=y' or 'CONFIG_FRED=0x123' or 'CONFIG_FRED="fred"'
- adjust_cfg (dict of str): Changes to make to .config file before
- building:
- key: str config to change, without the CONFIG_ prefix, e.g.
- FRED
- value: str change to make (C is config option without prefix):
- C to enable C
- ~C to disable C
- C=val to set the value of C (val must have quotes if C is
- a string Kconfig)
- done (set of set): Adds the config option to this set if it is changed
- in some way. This is used to track which ones have been processed.
- None to skip.
- Returns:
- tuple:
- str: New string for this line (maybe unchanged)
- str: Adjustment string that was used
- """
- out_line = line
- m_line = RE_LINE.match(line)
- adj = None
- if m_line:
- _, opt, _, _ = m_line.groups()
- adj = adjust_cfg.get(opt)
- if adj:
- out_line = make_cfg_line(opt, adj)
- if done is not None:
- done.add(opt)
- return out_line, adj
- def adjust_cfg_lines(lines, adjust_cfg):
- """Make adjustments to a list of lines from a .config file
- Args:
- lines (list of str): List of lines to process
- adjust_cfg (dict of str): Changes to make to .config file before
- building:
- key: str config to change, without the CONFIG_ prefix, e.g.
- FRED
- value: str change to make (C is config option without prefix):
- C to enable C
- ~C to disable C
- C=val to set the value of C (val must have quotes if C is
- a string Kconfig)
- Returns:
- list of str: New list of lines resulting from the processing
- """
- out_lines = []
- done = set()
- for line in lines:
- out_line, _ = adjust_cfg_line(line, adjust_cfg, done)
- out_lines.append(out_line)
- for opt in adjust_cfg:
- if opt not in done:
- adj = adjust_cfg.get(opt)
- out_line = make_cfg_line(opt, adj)
- out_lines.append(out_line)
- return out_lines
- def adjust_cfg_file(fname, adjust_cfg):
- """Make adjustments to a .config file
- Args:
- fname (str): Filename of .config file to change
- adjust_cfg (dict of str): Changes to make to .config file before
- building:
- key: str config to change, without the CONFIG_ prefix, e.g.
- FRED
- value: str change to make (C is config option without prefix):
- C to enable C
- ~C to disable C
- C=val to set the value of C (val must have quotes if C is
- a string Kconfig)
- """
- lines = tools.read_file(fname, binary=False).splitlines()
- out_lines = adjust_cfg_lines(lines, adjust_cfg)
- out = '\n'.join(out_lines) + '\n'
- tools.write_file(fname, out, binary=False)
- def convert_list_to_dict(adjust_cfg_list):
- """Convert a list of config changes into the dict used by adjust_cfg_file()
- Args:
- adjust_cfg_list (list of str): List of changes to make to .config file
- before building. Each is one of (where C is the config option with
- or without the CONFIG_ prefix)
- C to enable C
- ~C to disable C
- C=val to set the value of C (val must have quotes if C is
- a string Kconfig
- Returns:
- dict of str: Changes to make to .config file before building:
- key: str config to change, without the CONFIG_ prefix, e.g. FRED
- value: str change to make (C is config option without prefix):
- C to enable C
- ~C to disable C
- C=val to set the value of C (val must have quotes if C is
- a string Kconfig)
- Raises:
- ValueError: if an item in adjust_cfg_list has invalid syntax
- """
- result = {}
- for cfg in adjust_cfg_list or []:
- m_cfg = RE_CFG.match(cfg)
- if not m_cfg:
- raise ValueError(f"Invalid CONFIG adjustment '{cfg}'")
- negate, _, opt, val = m_cfg.groups()
- result[opt] = f'%s{opt}%s' % (negate or '', val or '')
- return result
- def check_cfg_lines(lines, adjust_cfg):
- """Check that lines do not conflict with the requested changes
- If a line enables a CONFIG which was requested to be disabled, etc., then
- this is an error. This function finds such errors.
- Args:
- lines (list of str): List of lines to process
- adjust_cfg (dict of str): Changes to make to .config file before
- building:
- key: str config to change, without the CONFIG_ prefix, e.g.
- FRED
- value: str change to make (C is config option without prefix):
- C to enable C
- ~C to disable C
- C=val to set the value of C (val must have quotes if C is
- a string Kconfig)
- Returns:
- list of tuple: list of errors, each a tuple:
- str: cfg adjustment requested
- str: line of the config that conflicts
- """
- bad = []
- done = set()
- for line in lines:
- out_line, adj = adjust_cfg_line(line, adjust_cfg, done)
- if out_line != line:
- bad.append([adj, line])
- for opt in adjust_cfg:
- if opt not in done:
- adj = adjust_cfg.get(opt)
- out_line = make_cfg_line(opt, adj)
- bad.append([adj, f'Missing expected line: {out_line}'])
- return bad
- def check_cfg_file(fname, adjust_cfg):
- """Check that a config file has been adjusted according to adjust_cfg
- Args:
- fname (str): Filename of .config file to change
- adjust_cfg (dict of str): Changes to make to .config file before
- building:
- key: str config to change, without the CONFIG_ prefix, e.g.
- FRED
- value: str change to make (C is config option without prefix):
- C to enable C
- ~C to disable C
- C=val to set the value of C (val must have quotes if C is
- a string Kconfig)
- Returns:
- str: None if OK, else an error string listing the problems
- """
- lines = tools.read_file(fname, binary=False).splitlines()
- bad_cfgs = check_cfg_lines(lines, adjust_cfg)
- if bad_cfgs:
- out = [f'{cfg:20} {line}' for cfg, line in bad_cfgs]
- content = '\\n'.join(out)
- return f'''
- Some CONFIG adjustments did not take effect. This may be because
- the request CONFIGs do not exist or conflict with others.
- Failed adjustments:
- {content}
- '''
- return None
|