Commit | Line | Data |
---|---|---|
6ebf5866 FG |
1 | # SPDX-License-Identifier: GPL-2.0 |
2 | # | |
3 | # Builds a .config from a kunitconfig. | |
4 | # | |
5 | # Copyright (C) 2019, Google LLC. | |
6 | # Author: Felix Guo <felixguoxiuping@gmail.com> | |
7 | # Author: Brendan Higgins <brendanhiggins@google.com> | |
8 | ||
e6f61920 | 9 | from dataclasses import dataclass |
6ebf5866 | 10 | import re |
1da2e622 | 11 | from typing import Any, Dict, Iterable, List, Tuple |
6ebf5866 | 12 | |
97752c39 | 13 | CONFIG_IS_NOT_SET_PATTERN = r'^# CONFIG_(\w+) is not set$' |
3f37d14b | 14 | CONFIG_PATTERN = r'^CONFIG_(\w+)=(\S+|".*")$' |
6ebf5866 | 15 | |
e6f61920 DL |
16 | @dataclass(frozen=True) |
17 | class KconfigEntry: | |
18 | name: str | |
19 | value: str | |
6ebf5866 FG |
20 | |
21 | def __str__(self) -> str: | |
97752c39 | 22 | if self.value == 'n': |
0453f984 DL |
23 | return f'# CONFIG_{self.name} is not set' |
24 | return f'CONFIG_{self.name}={self.value}' | |
6ebf5866 FG |
25 | |
26 | ||
27 | class KconfigParseError(Exception): | |
28 | """Error parsing Kconfig defconfig or .config.""" | |
29 | ||
30 | ||
0453f984 | 31 | class Kconfig: |
6ebf5866 FG |
32 | """Represents defconfig or .config specified using the Kconfig language.""" |
33 | ||
09641f7c | 34 | def __init__(self) -> None: |
8a7c6f85 | 35 | self._entries = {} # type: Dict[str, str] |
6ebf5866 | 36 | |
1da2e622 | 37 | def __eq__(self, other: Any) -> bool: |
8a7c6f85 DL |
38 | if not isinstance(other, self.__class__): |
39 | return False | |
40 | return self._entries == other._entries | |
6ebf5866 | 41 | |
8a7c6f85 DL |
42 | def __repr__(self) -> str: |
43 | return ','.join(str(e) for e in self.as_entries()) | |
44 | ||
45 | def as_entries(self) -> Iterable[KconfigEntry]: | |
46 | for name, value in self._entries.items(): | |
47 | yield KconfigEntry(name, value) | |
48 | ||
49 | def add_entry(self, name: str, value: str) -> None: | |
50 | self._entries[name] = value | |
6ebf5866 FG |
51 | |
52 | def is_subset_of(self, other: 'Kconfig') -> bool: | |
8a7c6f85 DL |
53 | for name, value in self._entries.items(): |
54 | b = other._entries.get(name) | |
d3bae4a0 | 55 | if b is None: |
8a7c6f85 | 56 | if value == 'n': |
97752c39 | 57 | continue |
d3bae4a0 | 58 | return False |
8a7c6f85 | 59 | if value != b: |
97752c39 DG |
60 | return False |
61 | return True | |
6ebf5866 | 62 | |
53b46621 DL |
63 | def conflicting_options(self, other: 'Kconfig') -> List[Tuple[KconfigEntry, KconfigEntry]]: |
64 | diff = [] # type: List[Tuple[KconfigEntry, KconfigEntry]] | |
65 | for name, value in self._entries.items(): | |
66 | b = other._entries.get(name) | |
67 | if b and value != b: | |
68 | pair = (KconfigEntry(name, value), KconfigEntry(name, b)) | |
69 | diff.append(pair) | |
70 | return diff | |
71 | ||
87c9c163 | 72 | def merge_in_entries(self, other: 'Kconfig') -> None: |
8a7c6f85 DL |
73 | for name, value in other._entries.items(): |
74 | self._entries[name] = value | |
87c9c163 | 75 | |
6ebf5866 | 76 | def write_to_file(self, path: str) -> None: |
87c9c163 | 77 | with open(path, 'a+') as f: |
8a7c6f85 DL |
78 | for e in self.as_entries(): |
79 | f.write(str(e) + '\n') | |
6ebf5866 | 80 | |
98978490 DL |
81 | def parse_file(path: str) -> Kconfig: |
82 | with open(path, 'r') as f: | |
83 | return parse_from_string(f.read()) | |
84 | ||
85 | def parse_from_string(blob: str) -> Kconfig: | |
86 | """Parses a string containing Kconfig entries.""" | |
87 | kconfig = Kconfig() | |
88 | is_not_set_matcher = re.compile(CONFIG_IS_NOT_SET_PATTERN) | |
89 | config_matcher = re.compile(CONFIG_PATTERN) | |
90 | for line in blob.split('\n'): | |
91 | line = line.strip() | |
92 | if not line: | |
93 | continue | |
94 | ||
95 | match = config_matcher.match(line) | |
96 | if match: | |
8a7c6f85 | 97 | kconfig.add_entry(match.group(1), match.group(2)) |
98978490 DL |
98 | continue |
99 | ||
100 | empty_match = is_not_set_matcher.match(line) | |
101 | if empty_match: | |
8a7c6f85 | 102 | kconfig.add_entry(empty_match.group(1), 'n') |
98978490 DL |
103 | continue |
104 | ||
105 | if line[0] == '#': | |
106 | continue | |
0453f984 | 107 | raise KconfigParseError('Failed to parse: ' + line) |
98978490 | 108 | return kconfig |