Merge tag 'u-boot-rockchip-20200501' of https://gitlab.denx.de/u-boot/custodians...
[platform/kernel/u-boot.git] / tools / patman / settings.py
1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2011 The Chromium OS Authors.
3 #
4
5 try:
6     import configparser as ConfigParser
7 except:
8     import ConfigParser
9
10 import os
11 import re
12
13 from patman import command
14 from patman import gitutil
15 from patman import tools
16
17 """Default settings per-project.
18
19 These are used by _ProjectConfigParser.  Settings names should match
20 the "dest" of the option parser from patman.py.
21 """
22 _default_settings = {
23     "u-boot": {},
24     "linux": {
25         "process_tags": "False",
26     }
27 }
28
29 class _ProjectConfigParser(ConfigParser.SafeConfigParser):
30     """ConfigParser that handles projects.
31
32     There are two main goals of this class:
33     - Load project-specific default settings.
34     - Merge general default settings/aliases with project-specific ones.
35
36     # Sample config used for tests below...
37     >>> from io import StringIO
38     >>> sample_config = '''
39     ... [alias]
40     ... me: Peter P. <likesspiders@example.com>
41     ... enemies: Evil <evil@example.com>
42     ...
43     ... [sm_alias]
44     ... enemies: Green G. <ugly@example.com>
45     ...
46     ... [sm2_alias]
47     ... enemies: Doc O. <pus@example.com>
48     ...
49     ... [settings]
50     ... am_hero: True
51     ... '''
52
53     # Check to make sure that bogus project gets general alias.
54     >>> config = _ProjectConfigParser("zzz")
55     >>> config.readfp(StringIO(sample_config))
56     >>> str(config.get("alias", "enemies"))
57     'Evil <evil@example.com>'
58
59     # Check to make sure that alias gets overridden by project.
60     >>> config = _ProjectConfigParser("sm")
61     >>> config.readfp(StringIO(sample_config))
62     >>> str(config.get("alias", "enemies"))
63     'Green G. <ugly@example.com>'
64
65     # Check to make sure that settings get merged with project.
66     >>> config = _ProjectConfigParser("linux")
67     >>> config.readfp(StringIO(sample_config))
68     >>> sorted((str(a), str(b)) for (a, b) in config.items("settings"))
69     [('am_hero', 'True'), ('process_tags', 'False')]
70
71     # Check to make sure that settings works with unknown project.
72     >>> config = _ProjectConfigParser("unknown")
73     >>> config.readfp(StringIO(sample_config))
74     >>> sorted((str(a), str(b)) for (a, b) in config.items("settings"))
75     [('am_hero', 'True')]
76     """
77     def __init__(self, project_name):
78         """Construct _ProjectConfigParser.
79
80         In addition to standard SafeConfigParser initialization, this also loads
81         project defaults.
82
83         Args:
84             project_name: The name of the project.
85         """
86         self._project_name = project_name
87         ConfigParser.SafeConfigParser.__init__(self)
88
89         # Update the project settings in the config based on
90         # the _default_settings global.
91         project_settings = "%s_settings" % project_name
92         if not self.has_section(project_settings):
93             self.add_section(project_settings)
94         project_defaults = _default_settings.get(project_name, {})
95         for setting_name, setting_value in project_defaults.items():
96             self.set(project_settings, setting_name, setting_value)
97
98     def get(self, section, option, *args, **kwargs):
99         """Extend SafeConfigParser to try project_section before section.
100
101         Args:
102             See SafeConfigParser.
103         Returns:
104             See SafeConfigParser.
105         """
106         try:
107             val = ConfigParser.SafeConfigParser.get(
108                 self, "%s_%s" % (self._project_name, section), option,
109                 *args, **kwargs
110             )
111         except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
112             val = ConfigParser.SafeConfigParser.get(
113                 self, section, option, *args, **kwargs
114             )
115         return tools.ToUnicode(val)
116
117     def items(self, section, *args, **kwargs):
118         """Extend SafeConfigParser to add project_section to section.
119
120         Args:
121             See SafeConfigParser.
122         Returns:
123             See SafeConfigParser.
124         """
125         project_items = []
126         has_project_section = False
127         top_items = []
128
129         # Get items from the project section
130         try:
131             project_items = ConfigParser.SafeConfigParser.items(
132                 self, "%s_%s" % (self._project_name, section), *args, **kwargs
133             )
134             has_project_section = True
135         except ConfigParser.NoSectionError:
136             pass
137
138         # Get top-level items
139         try:
140             top_items = ConfigParser.SafeConfigParser.items(
141                 self, section, *args, **kwargs
142             )
143         except ConfigParser.NoSectionError:
144             # If neither section exists raise the error on...
145             if not has_project_section:
146                 raise
147
148         item_dict = dict(top_items)
149         item_dict.update(project_items)
150         return {(tools.ToUnicode(item), tools.ToUnicode(val))
151                 for item, val in item_dict.items()}
152
153 def ReadGitAliases(fname):
154     """Read a git alias file. This is in the form used by git:
155
156     alias uboot  u-boot@lists.denx.de
157     alias wd     Wolfgang Denk <wd@denx.de>
158
159     Args:
160         fname: Filename to read
161     """
162     try:
163         fd = open(fname, 'r', encoding='utf-8')
164     except IOError:
165         print("Warning: Cannot find alias file '%s'" % fname)
166         return
167
168     re_line = re.compile('alias\s+(\S+)\s+(.*)')
169     for line in fd.readlines():
170         line = line.strip()
171         if not line or line[0] == '#':
172             continue
173
174         m = re_line.match(line)
175         if not m:
176             print("Warning: Alias file line '%s' not understood" % line)
177             continue
178
179         list = alias.get(m.group(1), [])
180         for item in m.group(2).split(','):
181             item = item.strip()
182             if item:
183                 list.append(item)
184         alias[m.group(1)] = list
185
186     fd.close()
187
188 def CreatePatmanConfigFile(config_fname):
189     """Creates a config file under $(HOME)/.patman if it can't find one.
190
191     Args:
192         config_fname: Default config filename i.e., $(HOME)/.patman
193
194     Returns:
195         None
196     """
197     name = gitutil.GetDefaultUserName()
198     if name == None:
199         name = raw_input("Enter name: ")
200
201     email = gitutil.GetDefaultUserEmail()
202
203     if email == None:
204         email = raw_input("Enter email: ")
205
206     try:
207         f = open(config_fname, 'w')
208     except IOError:
209         print("Couldn't create patman config file\n")
210         raise
211
212     print('''[alias]
213 me: %s <%s>
214
215 [bounces]
216 nxp = Zhikang Zhang <zhikang.zhang@nxp.com>
217 ''' % (name, email), file=f)
218     f.close();
219
220 def _UpdateDefaults(parser, config):
221     """Update the given OptionParser defaults based on config.
222
223     We'll walk through all of the settings from the parser
224     For each setting we'll look for a default in the option parser.
225     If it's found we'll update the option parser default.
226
227     The idea here is that the .patman file should be able to update
228     defaults but that command line flags should still have the final
229     say.
230
231     Args:
232         parser: An instance of an OptionParser whose defaults will be
233             updated.
234         config: An instance of _ProjectConfigParser that we will query
235             for settings.
236     """
237     defaults = parser.get_default_values()
238     for name, val in config.items('settings'):
239         if hasattr(defaults, name):
240             default_val = getattr(defaults, name)
241             if isinstance(default_val, bool):
242                 val = config.getboolean('settings', name)
243             elif isinstance(default_val, int):
244                 val = config.getint('settings', name)
245             parser.set_default(name, val)
246         else:
247             print("WARNING: Unknown setting %s" % name)
248
249 def _ReadAliasFile(fname):
250     """Read in the U-Boot git alias file if it exists.
251
252     Args:
253         fname: Filename to read.
254     """
255     if os.path.exists(fname):
256         bad_line = None
257         with open(fname, encoding='utf-8') as fd:
258             linenum = 0
259             for line in fd:
260                 linenum += 1
261                 line = line.strip()
262                 if not line or line.startswith('#'):
263                     continue
264                 words = line.split(None, 2)
265                 if len(words) < 3 or words[0] != 'alias':
266                     if not bad_line:
267                         bad_line = "%s:%d:Invalid line '%s'" % (fname, linenum,
268                                                                 line)
269                     continue
270                 alias[words[1]] = [s.strip() for s in words[2].split(',')]
271         if bad_line:
272             print(bad_line)
273
274 def _ReadBouncesFile(fname):
275     """Read in the bounces file if it exists
276
277     Args:
278         fname: Filename to read.
279     """
280     if os.path.exists(fname):
281         with open(fname) as fd:
282             for line in fd:
283                 if line.startswith('#'):
284                     continue
285                 bounces.add(line.strip())
286
287 def GetItems(config, section):
288     """Get the items from a section of the config.
289
290     Args:
291         config: _ProjectConfigParser object containing settings
292         section: name of section to retrieve
293
294     Returns:
295         List of (name, value) tuples for the section
296     """
297     try:
298         return config.items(section)
299     except ConfigParser.NoSectionError as e:
300         return []
301     except:
302         raise
303
304 def Setup(parser, project_name, config_fname=''):
305     """Set up the settings module by reading config files.
306
307     Args:
308         parser:         The parser to update
309         project_name:   Name of project that we're working on; we'll look
310             for sections named "project_section" as well.
311         config_fname:   Config filename to read ('' for default)
312     """
313     # First read the git alias file if available
314     _ReadAliasFile('doc/git-mailrc')
315     config = _ProjectConfigParser(project_name)
316     if config_fname == '':
317         config_fname = '%s/.patman' % os.getenv('HOME')
318
319     if not os.path.exists(config_fname):
320         print("No config file found ~/.patman\nCreating one...\n")
321         CreatePatmanConfigFile(config_fname)
322
323     config.read(config_fname)
324
325     for name, value in GetItems(config, 'alias'):
326         alias[name] = value.split(',')
327
328     _ReadBouncesFile('doc/bounces')
329     for name, value in GetItems(config, 'bounces'):
330         bounces.add(value)
331
332     _UpdateDefaults(parser, config)
333
334 # These are the aliases we understand, indexed by alias. Each member is a list.
335 alias = {}
336 bounces = set()
337
338 if __name__ == "__main__":
339     import doctest
340
341     doctest.testmod()