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