1*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0 2*4882a593Smuzhiyun# 3*4882a593Smuzhiyun# Builds a .config from a kunitconfig. 4*4882a593Smuzhiyun# 5*4882a593Smuzhiyun# Copyright (C) 2019, Google LLC. 6*4882a593Smuzhiyun# Author: Felix Guo <felixguoxiuping@gmail.com> 7*4882a593Smuzhiyun# Author: Brendan Higgins <brendanhiggins@google.com> 8*4882a593Smuzhiyun 9*4882a593Smuzhiyunimport collections 10*4882a593Smuzhiyunimport re 11*4882a593Smuzhiyun 12*4882a593SmuzhiyunCONFIG_IS_NOT_SET_PATTERN = r'^# CONFIG_(\w+) is not set$' 13*4882a593SmuzhiyunCONFIG_PATTERN = r'^CONFIG_(\w+)=(\S+|".*")$' 14*4882a593Smuzhiyun 15*4882a593SmuzhiyunKconfigEntryBase = collections.namedtuple('KconfigEntryBase', ['name', 'value']) 16*4882a593Smuzhiyun 17*4882a593Smuzhiyunclass KconfigEntry(KconfigEntryBase): 18*4882a593Smuzhiyun 19*4882a593Smuzhiyun def __str__(self) -> str: 20*4882a593Smuzhiyun if self.value == 'n': 21*4882a593Smuzhiyun return r'# CONFIG_%s is not set' % (self.name) 22*4882a593Smuzhiyun else: 23*4882a593Smuzhiyun return r'CONFIG_%s=%s' % (self.name, self.value) 24*4882a593Smuzhiyun 25*4882a593Smuzhiyun 26*4882a593Smuzhiyunclass KconfigParseError(Exception): 27*4882a593Smuzhiyun """Error parsing Kconfig defconfig or .config.""" 28*4882a593Smuzhiyun 29*4882a593Smuzhiyun 30*4882a593Smuzhiyunclass Kconfig(object): 31*4882a593Smuzhiyun """Represents defconfig or .config specified using the Kconfig language.""" 32*4882a593Smuzhiyun 33*4882a593Smuzhiyun def __init__(self): 34*4882a593Smuzhiyun self._entries = [] 35*4882a593Smuzhiyun 36*4882a593Smuzhiyun def entries(self): 37*4882a593Smuzhiyun return set(self._entries) 38*4882a593Smuzhiyun 39*4882a593Smuzhiyun def add_entry(self, entry: KconfigEntry) -> None: 40*4882a593Smuzhiyun self._entries.append(entry) 41*4882a593Smuzhiyun 42*4882a593Smuzhiyun def is_subset_of(self, other: 'Kconfig') -> bool: 43*4882a593Smuzhiyun for a in self.entries(): 44*4882a593Smuzhiyun found = False 45*4882a593Smuzhiyun for b in other.entries(): 46*4882a593Smuzhiyun if a.name != b.name: 47*4882a593Smuzhiyun continue 48*4882a593Smuzhiyun if a.value != b.value: 49*4882a593Smuzhiyun return False 50*4882a593Smuzhiyun found = True 51*4882a593Smuzhiyun if a.value != 'n' and found == False: 52*4882a593Smuzhiyun return False 53*4882a593Smuzhiyun return True 54*4882a593Smuzhiyun 55*4882a593Smuzhiyun def write_to_file(self, path: str) -> None: 56*4882a593Smuzhiyun with open(path, 'w') as f: 57*4882a593Smuzhiyun for entry in self.entries(): 58*4882a593Smuzhiyun f.write(str(entry) + '\n') 59*4882a593Smuzhiyun 60*4882a593Smuzhiyun def parse_from_string(self, blob: str) -> None: 61*4882a593Smuzhiyun """Parses a string containing KconfigEntrys and populates this Kconfig.""" 62*4882a593Smuzhiyun self._entries = [] 63*4882a593Smuzhiyun is_not_set_matcher = re.compile(CONFIG_IS_NOT_SET_PATTERN) 64*4882a593Smuzhiyun config_matcher = re.compile(CONFIG_PATTERN) 65*4882a593Smuzhiyun for line in blob.split('\n'): 66*4882a593Smuzhiyun line = line.strip() 67*4882a593Smuzhiyun if not line: 68*4882a593Smuzhiyun continue 69*4882a593Smuzhiyun 70*4882a593Smuzhiyun match = config_matcher.match(line) 71*4882a593Smuzhiyun if match: 72*4882a593Smuzhiyun entry = KconfigEntry(match.group(1), match.group(2)) 73*4882a593Smuzhiyun self.add_entry(entry) 74*4882a593Smuzhiyun continue 75*4882a593Smuzhiyun 76*4882a593Smuzhiyun empty_match = is_not_set_matcher.match(line) 77*4882a593Smuzhiyun if empty_match: 78*4882a593Smuzhiyun entry = KconfigEntry(empty_match.group(1), 'n') 79*4882a593Smuzhiyun self.add_entry(entry) 80*4882a593Smuzhiyun continue 81*4882a593Smuzhiyun 82*4882a593Smuzhiyun if line[0] == '#': 83*4882a593Smuzhiyun continue 84*4882a593Smuzhiyun else: 85*4882a593Smuzhiyun raise KconfigParseError('Failed to parse: ' + line) 86*4882a593Smuzhiyun 87*4882a593Smuzhiyun def read_from_file(self, path: str) -> None: 88*4882a593Smuzhiyun with open(path, 'r') as f: 89*4882a593Smuzhiyun self.parse_from_string(f.read()) 90