xref: /openbmc/u-boot/tools/patman/settings.py (revision b1e6c4c3)
1# Copyright (c) 2011 The Chromium OS Authors.
2#
3# See file CREDITS for list of people who contributed to this
4# project.
5#
6# This program is free software; you can redistribute it and/or
7# modify it under the terms of the GNU General Public License as
8# published by the Free Software Foundation; either version 2 of
9# the License, or (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
19# MA 02111-1307 USA
20#
21
22import ConfigParser
23import os
24import re
25
26import command
27import gitutil
28
29"""Default settings per-project.
30
31These are used by _ProjectConfigParser.  Settings names should match
32the "dest" of the option parser from patman.py.
33"""
34_default_settings = {
35    "u-boot": {},
36    "linux": {
37        "process_tags": "False",
38    }
39}
40
41class _ProjectConfigParser(ConfigParser.SafeConfigParser):
42    """ConfigParser that handles projects.
43
44    There are two main goals of this class:
45    - Load project-specific default settings.
46    - Merge general default settings/aliases with project-specific ones.
47
48    # Sample config used for tests below...
49    >>> import StringIO
50    >>> sample_config = '''
51    ... [alias]
52    ... me: Peter P. <likesspiders@example.com>
53    ... enemies: Evil <evil@example.com>
54    ...
55    ... [sm_alias]
56    ... enemies: Green G. <ugly@example.com>
57    ...
58    ... [sm2_alias]
59    ... enemies: Doc O. <pus@example.com>
60    ...
61    ... [settings]
62    ... am_hero: True
63    ... '''
64
65    # Check to make sure that bogus project gets general alias.
66    >>> config = _ProjectConfigParser("zzz")
67    >>> config.readfp(StringIO.StringIO(sample_config))
68    >>> config.get("alias", "enemies")
69    'Evil <evil@example.com>'
70
71    # Check to make sure that alias gets overridden by project.
72    >>> config = _ProjectConfigParser("sm")
73    >>> config.readfp(StringIO.StringIO(sample_config))
74    >>> config.get("alias", "enemies")
75    'Green G. <ugly@example.com>'
76
77    # Check to make sure that settings get merged with project.
78    >>> config = _ProjectConfigParser("linux")
79    >>> config.readfp(StringIO.StringIO(sample_config))
80    >>> sorted(config.items("settings"))
81    [('am_hero', 'True'), ('process_tags', 'False')]
82
83    # Check to make sure that settings works with unknown project.
84    >>> config = _ProjectConfigParser("unknown")
85    >>> config.readfp(StringIO.StringIO(sample_config))
86    >>> sorted(config.items("settings"))
87    [('am_hero', 'True')]
88    """
89    def __init__(self, project_name):
90        """Construct _ProjectConfigParser.
91
92        In addition to standard SafeConfigParser initialization, this also loads
93        project defaults.
94
95        Args:
96            project_name: The name of the project.
97        """
98        self._project_name = project_name
99        ConfigParser.SafeConfigParser.__init__(self)
100
101        # Update the project settings in the config based on
102        # the _default_settings global.
103        project_settings = "%s_settings" % project_name
104        if not self.has_section(project_settings):
105            self.add_section(project_settings)
106        project_defaults = _default_settings.get(project_name, {})
107        for setting_name, setting_value in project_defaults.iteritems():
108            self.set(project_settings, setting_name, setting_value)
109
110    def get(self, section, option, *args, **kwargs):
111        """Extend SafeConfigParser to try project_section before section.
112
113        Args:
114            See SafeConfigParser.
115        Returns:
116            See SafeConfigParser.
117        """
118        try:
119            return ConfigParser.SafeConfigParser.get(
120                self, "%s_%s" % (self._project_name, section), option,
121                *args, **kwargs
122            )
123        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
124            return ConfigParser.SafeConfigParser.get(
125                self, section, option, *args, **kwargs
126            )
127
128    def items(self, section, *args, **kwargs):
129        """Extend SafeConfigParser to add project_section to section.
130
131        Args:
132            See SafeConfigParser.
133        Returns:
134            See SafeConfigParser.
135        """
136        project_items = []
137        has_project_section = False
138        top_items = []
139
140        # Get items from the project section
141        try:
142            project_items = ConfigParser.SafeConfigParser.items(
143                self, "%s_%s" % (self._project_name, section), *args, **kwargs
144            )
145            has_project_section = True
146        except ConfigParser.NoSectionError:
147            pass
148
149        # Get top-level items
150        try:
151            top_items = ConfigParser.SafeConfigParser.items(
152                self, section, *args, **kwargs
153            )
154        except ConfigParser.NoSectionError:
155            # If neither section exists raise the error on...
156            if not has_project_section:
157                raise
158
159        item_dict = dict(top_items)
160        item_dict.update(project_items)
161        return item_dict.items()
162
163def ReadGitAliases(fname):
164    """Read a git alias file. This is in the form used by git:
165
166    alias uboot  u-boot@lists.denx.de
167    alias wd     Wolfgang Denk <wd@denx.de>
168
169    Args:
170        fname: Filename to read
171    """
172    try:
173        fd = open(fname, 'r')
174    except IOError:
175        print "Warning: Cannot find alias file '%s'" % fname
176        return
177
178    re_line = re.compile('alias\s+(\S+)\s+(.*)')
179    for line in fd.readlines():
180        line = line.strip()
181        if not line or line[0] == '#':
182            continue
183
184        m = re_line.match(line)
185        if not m:
186            print "Warning: Alias file line '%s' not understood" % line
187            continue
188
189        list = alias.get(m.group(1), [])
190        for item in m.group(2).split(','):
191            item = item.strip()
192            if item:
193                list.append(item)
194        alias[m.group(1)] = list
195
196    fd.close()
197
198def CreatePatmanConfigFile(config_fname):
199    """Creates a config file under $(HOME)/.patman if it can't find one.
200
201    Args:
202        config_fname: Default config filename i.e., $(HOME)/.patman
203
204    Returns:
205        None
206    """
207    name = gitutil.GetDefaultUserName()
208    if name == None:
209        name = raw_input("Enter name: ")
210
211    email = gitutil.GetDefaultUserEmail()
212
213    if email == None:
214        email = raw_input("Enter email: ")
215
216    try:
217        f = open(config_fname, 'w')
218    except IOError:
219        print "Couldn't create patman config file\n"
220        raise
221
222    print >>f, "[alias]\nme: %s <%s>" % (name, email)
223    f.close();
224
225def _UpdateDefaults(parser, config):
226    """Update the given OptionParser defaults based on config.
227
228    We'll walk through all of the settings from the parser
229    For each setting we'll look for a default in the option parser.
230    If it's found we'll update the option parser default.
231
232    The idea here is that the .patman file should be able to update
233    defaults but that command line flags should still have the final
234    say.
235
236    Args:
237        parser: An instance of an OptionParser whose defaults will be
238            updated.
239        config: An instance of _ProjectConfigParser that we will query
240            for settings.
241    """
242    defaults = parser.get_default_values()
243    for name, val in config.items('settings'):
244        if hasattr(defaults, name):
245            default_val = getattr(defaults, name)
246            if isinstance(default_val, bool):
247                val = config.getboolean('settings', name)
248            elif isinstance(default_val, int):
249                val = config.getint('settings', name)
250            parser.set_default(name, val)
251        else:
252            print "WARNING: Unknown setting %s" % name
253
254def Setup(parser, project_name, config_fname=''):
255    """Set up the settings module by reading config files.
256
257    Args:
258        parser:         The parser to update
259        project_name:   Name of project that we're working on; we'll look
260            for sections named "project_section" as well.
261        config_fname:   Config filename to read ('' for default)
262    """
263    config = _ProjectConfigParser(project_name)
264    if config_fname == '':
265        config_fname = '%s/.patman' % os.getenv('HOME')
266
267    if not os.path.exists(config_fname):
268        print "No config file found ~/.patman\nCreating one...\n"
269        CreatePatmanConfigFile(config_fname)
270
271    config.read(config_fname)
272
273    for name, value in config.items('alias'):
274        alias[name] = value.split(',')
275
276    _UpdateDefaults(parser, config)
277
278# These are the aliases we understand, indexed by alias. Each member is a list.
279alias = {}
280
281if __name__ == "__main__":
282    import doctest
283
284    doctest.testmod()
285