xref: /openbmc/u-boot/tools/patman/settings.py (revision 3765b3e7)
1# Copyright (c) 2011 The Chromium OS Authors.
2#
3# SPDX-License-Identifier:	GPL-2.0+
4#
5
6import ConfigParser
7import os
8import re
9
10import command
11import gitutil
12
13"""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
147def 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
182def 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
209def _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.
223        config: An instance of _ProjectConfigParser that we will query
224            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
238def Setup(parser, project_name, config_fname=''):
239    """Set up the settings module by reading config files.
240
241    Args:
242        parser:         The parser to update
243        project_name:   Name of project that we're working on; we'll look
244            for sections named "project_section" as well.
245        config_fname:   Config filename to read ('' for default)
246    """
247    config = _ProjectConfigParser(project_name)
248    if config_fname == '':
249        config_fname = '%s/.patman' % os.getenv('HOME')
250
251    if not os.path.exists(config_fname):
252        print "No config file found ~/.patman\nCreating one...\n"
253        CreatePatmanConfigFile(config_fname)
254
255    config.read(config_fname)
256
257    for name, value in config.items('alias'):
258        alias[name] = value.split(',')
259
260    _UpdateDefaults(parser, config)
261
262# These are the aliases we understand, indexed by alias. Each member is a list.
263alias = {}
264
265if __name__ == "__main__":
266    import doctest
267
268    doctest.testmod()
269