blob: 01f6c38c19ff2c28e0fb33a6b859251dd26efcaf [file] [log] [blame]
Simon Glass0d24de92012-01-14 15:12:45 +00001# Copyright (c) 2011 The Chromium OS Authors.
2#
Wolfgang Denk1a459662013-07-08 09:37:19 +02003# SPDX-License-Identifier: GPL-2.0+
Simon Glass0d24de92012-01-14 15:12:45 +00004#
5
Paul Burtona920a172016-09-27 16:03:50 +01006from __future__ import print_function
7
Simon Glass0d24de92012-01-14 15:12:45 +00008import ConfigParser
9import os
10import re
11
12import command
Vikram Narayanan87d65552012-05-23 09:01:06 +000013import gitutil
Simon Glass0d24de92012-01-14 15:12:45 +000014
Doug Andersona1dcee82012-12-03 14:43:18 +000015"""Default settings per-project.
16
17These are used by _ProjectConfigParser. Settings names should match
18the "dest" of the option parser from patman.py.
19"""
20_default_settings = {
21 "u-boot": {},
22 "linux": {
23 "process_tags": "False",
24 }
25}
26
27class _ProjectConfigParser(ConfigParser.SafeConfigParser):
28 """ConfigParser that handles projects.
29
30 There are two main goals of this class:
31 - Load project-specific default settings.
32 - Merge general default settings/aliases with project-specific ones.
33
34 # Sample config used for tests below...
35 >>> import StringIO
36 >>> sample_config = '''
37 ... [alias]
38 ... me: Peter P. <likesspiders@example.com>
39 ... enemies: Evil <evil@example.com>
40 ...
41 ... [sm_alias]
42 ... enemies: Green G. <ugly@example.com>
43 ...
44 ... [sm2_alias]
45 ... enemies: Doc O. <pus@example.com>
46 ...
47 ... [settings]
48 ... am_hero: True
49 ... '''
50
51 # Check to make sure that bogus project gets general alias.
52 >>> config = _ProjectConfigParser("zzz")
53 >>> config.readfp(StringIO.StringIO(sample_config))
54 >>> config.get("alias", "enemies")
55 'Evil <evil@example.com>'
56
57 # Check to make sure that alias gets overridden by project.
58 >>> config = _ProjectConfigParser("sm")
59 >>> config.readfp(StringIO.StringIO(sample_config))
60 >>> config.get("alias", "enemies")
61 'Green G. <ugly@example.com>'
62
63 # Check to make sure that settings get merged with project.
64 >>> config = _ProjectConfigParser("linux")
65 >>> config.readfp(StringIO.StringIO(sample_config))
66 >>> sorted(config.items("settings"))
67 [('am_hero', 'True'), ('process_tags', 'False')]
68
69 # Check to make sure that settings works with unknown project.
70 >>> config = _ProjectConfigParser("unknown")
71 >>> config.readfp(StringIO.StringIO(sample_config))
72 >>> sorted(config.items("settings"))
73 [('am_hero', 'True')]
74 """
75 def __init__(self, project_name):
76 """Construct _ProjectConfigParser.
77
78 In addition to standard SafeConfigParser initialization, this also loads
79 project defaults.
80
81 Args:
82 project_name: The name of the project.
83 """
84 self._project_name = project_name
85 ConfigParser.SafeConfigParser.__init__(self)
86
87 # Update the project settings in the config based on
88 # the _default_settings global.
89 project_settings = "%s_settings" % project_name
90 if not self.has_section(project_settings):
91 self.add_section(project_settings)
92 project_defaults = _default_settings.get(project_name, {})
93 for setting_name, setting_value in project_defaults.iteritems():
94 self.set(project_settings, setting_name, setting_value)
95
96 def get(self, section, option, *args, **kwargs):
97 """Extend SafeConfigParser to try project_section before section.
98
99 Args:
100 See SafeConfigParser.
101 Returns:
102 See SafeConfigParser.
103 """
104 try:
105 return ConfigParser.SafeConfigParser.get(
106 self, "%s_%s" % (self._project_name, section), option,
107 *args, **kwargs
108 )
109 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
110 return ConfigParser.SafeConfigParser.get(
111 self, section, option, *args, **kwargs
112 )
113
114 def items(self, section, *args, **kwargs):
115 """Extend SafeConfigParser to add project_section to section.
116
117 Args:
118 See SafeConfigParser.
119 Returns:
120 See SafeConfigParser.
121 """
122 project_items = []
123 has_project_section = False
124 top_items = []
125
126 # Get items from the project section
127 try:
128 project_items = ConfigParser.SafeConfigParser.items(
129 self, "%s_%s" % (self._project_name, section), *args, **kwargs
130 )
131 has_project_section = True
132 except ConfigParser.NoSectionError:
133 pass
134
135 # Get top-level items
136 try:
137 top_items = ConfigParser.SafeConfigParser.items(
138 self, section, *args, **kwargs
139 )
140 except ConfigParser.NoSectionError:
141 # If neither section exists raise the error on...
142 if not has_project_section:
143 raise
144
145 item_dict = dict(top_items)
146 item_dict.update(project_items)
147 return item_dict.items()
148
Simon Glass0d24de92012-01-14 15:12:45 +0000149def ReadGitAliases(fname):
150 """Read a git alias file. This is in the form used by git:
151
152 alias uboot u-boot@lists.denx.de
153 alias wd Wolfgang Denk <wd@denx.de>
154
155 Args:
156 fname: Filename to read
157 """
158 try:
159 fd = open(fname, 'r')
160 except IOError:
Paul Burtona920a172016-09-27 16:03:50 +0100161 print("Warning: Cannot find alias file '%s'" % fname)
Simon Glass0d24de92012-01-14 15:12:45 +0000162 return
163
164 re_line = re.compile('alias\s+(\S+)\s+(.*)')
165 for line in fd.readlines():
166 line = line.strip()
167 if not line or line[0] == '#':
168 continue
169
170 m = re_line.match(line)
171 if not m:
Paul Burtona920a172016-09-27 16:03:50 +0100172 print("Warning: Alias file line '%s' not understood" % line)
Simon Glass0d24de92012-01-14 15:12:45 +0000173 continue
174
175 list = alias.get(m.group(1), [])
176 for item in m.group(2).split(','):
177 item = item.strip()
178 if item:
179 list.append(item)
180 alias[m.group(1)] = list
181
182 fd.close()
183
Vikram Narayanan87d65552012-05-23 09:01:06 +0000184def CreatePatmanConfigFile(config_fname):
185 """Creates a config file under $(HOME)/.patman if it can't find one.
186
187 Args:
188 config_fname: Default config filename i.e., $(HOME)/.patman
189
190 Returns:
191 None
192 """
193 name = gitutil.GetDefaultUserName()
194 if name == None:
195 name = raw_input("Enter name: ")
196
197 email = gitutil.GetDefaultUserEmail()
198
199 if email == None:
200 email = raw_input("Enter email: ")
201
202 try:
203 f = open(config_fname, 'w')
204 except IOError:
Paul Burtona920a172016-09-27 16:03:50 +0100205 print("Couldn't create patman config file\n")
Vikram Narayanan87d65552012-05-23 09:01:06 +0000206 raise
207
Paul Burtona920a172016-09-27 16:03:50 +0100208 print("[alias]\nme: %s <%s>" % (name, email), file=f)
Vikram Narayanan87d65552012-05-23 09:01:06 +0000209 f.close();
210
Doug Anderson8568bae2012-12-03 14:43:17 +0000211def _UpdateDefaults(parser, config):
212 """Update the given OptionParser defaults based on config.
213
214 We'll walk through all of the settings from the parser
215 For each setting we'll look for a default in the option parser.
216 If it's found we'll update the option parser default.
217
218 The idea here is that the .patman file should be able to update
219 defaults but that command line flags should still have the final
220 say.
221
222 Args:
223 parser: An instance of an OptionParser whose defaults will be
224 updated.
Doug Andersona1dcee82012-12-03 14:43:18 +0000225 config: An instance of _ProjectConfigParser that we will query
Doug Anderson8568bae2012-12-03 14:43:17 +0000226 for settings.
227 """
228 defaults = parser.get_default_values()
229 for name, val in config.items('settings'):
230 if hasattr(defaults, name):
231 default_val = getattr(defaults, name)
232 if isinstance(default_val, bool):
233 val = config.getboolean('settings', name)
234 elif isinstance(default_val, int):
235 val = config.getint('settings', name)
236 parser.set_default(name, val)
237 else:
Paul Burtona920a172016-09-27 16:03:50 +0100238 print("WARNING: Unknown setting %s" % name)
Doug Anderson8568bae2012-12-03 14:43:17 +0000239
Simon Glass8895b3e2015-01-29 11:35:17 -0700240def _ReadAliasFile(fname):
241 """Read in the U-Boot git alias file if it exists.
242
243 Args:
244 fname: Filename to read.
245 """
246 if os.path.exists(fname):
247 bad_line = None
248 with open(fname) as fd:
249 linenum = 0
250 for line in fd:
251 linenum += 1
252 line = line.strip()
253 if not line or line.startswith('#'):
254 continue
255 words = line.split(' ', 2)
256 if len(words) < 3 or words[0] != 'alias':
257 if not bad_line:
258 bad_line = "%s:%d:Invalid line '%s'" % (fname, linenum,
259 line)
260 continue
261 alias[words[1]] = [s.strip() for s in words[2].split(',')]
262 if bad_line:
Paul Burtona920a172016-09-27 16:03:50 +0100263 print(bad_line)
Simon Glass8895b3e2015-01-29 11:35:17 -0700264
Doug Andersona1dcee82012-12-03 14:43:18 +0000265def Setup(parser, project_name, config_fname=''):
Simon Glass0d24de92012-01-14 15:12:45 +0000266 """Set up the settings module by reading config files.
267
268 Args:
Doug Anderson8568bae2012-12-03 14:43:17 +0000269 parser: The parser to update
Doug Andersona1dcee82012-12-03 14:43:18 +0000270 project_name: Name of project that we're working on; we'll look
271 for sections named "project_section" as well.
Simon Glass0d24de92012-01-14 15:12:45 +0000272 config_fname: Config filename to read ('' for default)
273 """
Simon Glass8895b3e2015-01-29 11:35:17 -0700274 # First read the git alias file if available
275 _ReadAliasFile('doc/git-mailrc')
Doug Andersona1dcee82012-12-03 14:43:18 +0000276 config = _ProjectConfigParser(project_name)
Simon Glass0d24de92012-01-14 15:12:45 +0000277 if config_fname == '':
Vikram Narayanan2b36c752012-05-23 08:58:58 +0000278 config_fname = '%s/.patman' % os.getenv('HOME')
Vikram Narayanan87d65552012-05-23 09:01:06 +0000279
280 if not os.path.exists(config_fname):
Paul Burtona920a172016-09-27 16:03:50 +0100281 print("No config file found ~/.patman\nCreating one...\n")
Vikram Narayanan87d65552012-05-23 09:01:06 +0000282 CreatePatmanConfigFile(config_fname)
283
Doug Anderson8568bae2012-12-03 14:43:17 +0000284 config.read(config_fname)
Simon Glass0d24de92012-01-14 15:12:45 +0000285
Doug Anderson8568bae2012-12-03 14:43:17 +0000286 for name, value in config.items('alias'):
Simon Glass0d24de92012-01-14 15:12:45 +0000287 alias[name] = value.split(',')
288
Doug Anderson8568bae2012-12-03 14:43:17 +0000289 _UpdateDefaults(parser, config)
Simon Glass0d24de92012-01-14 15:12:45 +0000290
291# These are the aliases we understand, indexed by alias. Each member is a list.
292alias = {}
Doug Andersona1dcee82012-12-03 14:43:18 +0000293
294if __name__ == "__main__":
295 import doctest
296
297 doctest.testmod()