18c2ecf20Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci# 38c2ecf20Sopenharmony_ci# Builds a .config from a kunitconfig. 48c2ecf20Sopenharmony_ci# 58c2ecf20Sopenharmony_ci# Copyright (C) 2019, Google LLC. 68c2ecf20Sopenharmony_ci# Author: Felix Guo <felixguoxiuping@gmail.com> 78c2ecf20Sopenharmony_ci# Author: Brendan Higgins <brendanhiggins@google.com> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ciimport collections 108c2ecf20Sopenharmony_ciimport re 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ciCONFIG_IS_NOT_SET_PATTERN = r'^# CONFIG_(\w+) is not set$' 138c2ecf20Sopenharmony_ciCONFIG_PATTERN = r'^CONFIG_(\w+)=(\S+|".*")$' 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ciKconfigEntryBase = collections.namedtuple('KconfigEntryBase', ['name', 'value']) 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ciclass KconfigEntry(KconfigEntryBase): 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci def __str__(self) -> str: 208c2ecf20Sopenharmony_ci if self.value == 'n': 218c2ecf20Sopenharmony_ci return r'# CONFIG_%s is not set' % (self.name) 228c2ecf20Sopenharmony_ci else: 238c2ecf20Sopenharmony_ci return r'CONFIG_%s=%s' % (self.name, self.value) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciclass KconfigParseError(Exception): 278c2ecf20Sopenharmony_ci """Error parsing Kconfig defconfig or .config.""" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciclass Kconfig(object): 318c2ecf20Sopenharmony_ci """Represents defconfig or .config specified using the Kconfig language.""" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci def __init__(self): 348c2ecf20Sopenharmony_ci self._entries = [] 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci def entries(self): 378c2ecf20Sopenharmony_ci return set(self._entries) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci def add_entry(self, entry: KconfigEntry) -> None: 408c2ecf20Sopenharmony_ci self._entries.append(entry) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci def is_subset_of(self, other: 'Kconfig') -> bool: 438c2ecf20Sopenharmony_ci for a in self.entries(): 448c2ecf20Sopenharmony_ci found = False 458c2ecf20Sopenharmony_ci for b in other.entries(): 468c2ecf20Sopenharmony_ci if a.name != b.name: 478c2ecf20Sopenharmony_ci continue 488c2ecf20Sopenharmony_ci if a.value != b.value: 498c2ecf20Sopenharmony_ci return False 508c2ecf20Sopenharmony_ci found = True 518c2ecf20Sopenharmony_ci if a.value != 'n' and found == False: 528c2ecf20Sopenharmony_ci return False 538c2ecf20Sopenharmony_ci return True 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci def write_to_file(self, path: str) -> None: 568c2ecf20Sopenharmony_ci with open(path, 'w') as f: 578c2ecf20Sopenharmony_ci for entry in self.entries(): 588c2ecf20Sopenharmony_ci f.write(str(entry) + '\n') 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci def parse_from_string(self, blob: str) -> None: 618c2ecf20Sopenharmony_ci """Parses a string containing KconfigEntrys and populates this Kconfig.""" 628c2ecf20Sopenharmony_ci self._entries = [] 638c2ecf20Sopenharmony_ci is_not_set_matcher = re.compile(CONFIG_IS_NOT_SET_PATTERN) 648c2ecf20Sopenharmony_ci config_matcher = re.compile(CONFIG_PATTERN) 658c2ecf20Sopenharmony_ci for line in blob.split('\n'): 668c2ecf20Sopenharmony_ci line = line.strip() 678c2ecf20Sopenharmony_ci if not line: 688c2ecf20Sopenharmony_ci continue 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci match = config_matcher.match(line) 718c2ecf20Sopenharmony_ci if match: 728c2ecf20Sopenharmony_ci entry = KconfigEntry(match.group(1), match.group(2)) 738c2ecf20Sopenharmony_ci self.add_entry(entry) 748c2ecf20Sopenharmony_ci continue 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci empty_match = is_not_set_matcher.match(line) 778c2ecf20Sopenharmony_ci if empty_match: 788c2ecf20Sopenharmony_ci entry = KconfigEntry(empty_match.group(1), 'n') 798c2ecf20Sopenharmony_ci self.add_entry(entry) 808c2ecf20Sopenharmony_ci continue 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if line[0] == '#': 838c2ecf20Sopenharmony_ci continue 848c2ecf20Sopenharmony_ci else: 858c2ecf20Sopenharmony_ci raise KconfigParseError('Failed to parse: ' + line) 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci def read_from_file(self, path: str) -> None: 888c2ecf20Sopenharmony_ci with open(path, 'r') as f: 898c2ecf20Sopenharmony_ci self.parse_from_string(f.read()) 90