xref: /rk3399_rockchip-uboot/tools/patman/settings.py (revision a920a17b2f418535870788ae81234dc6b8aa6661)
1# Copyright (c) 2011 The Chromium OS Authors.
2#
3# SPDX-License-Identifier:	GPL-2.0+
4#
5
6from __future__ import print_function
7
8import ConfigParser
9import os
10import re
11
12import command
13import gitutil
14
15"""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
149def 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:
161        print("Warning: Cannot find alias file '%s'" % fname)
162        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:
172            print("Warning: Alias file line '%s' not understood" % line)
173            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
184def 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:
205        print("Couldn't create patman config file\n")
206        raise
207
208    print("[alias]\nme: %s <%s>" % (name, email), file=f)
209    f.close();
210
211def _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.
225        config: An instance of _ProjectConfigParser that we will query
226            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:
238            print("WARNING: Unknown setting %s" % name)
239
240def _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:
263            print(bad_line)
264
265def Setup(parser, project_name, config_fname=''):
266    """Set up the settings module by reading config files.
267
268    Args:
269        parser:         The parser to update
270        project_name:   Name of project that we're working on; we'll look
271            for sections named "project_section" as well.
272        config_fname:   Config filename to read ('' for default)
273    """
274    # First read the git alias file if available
275    _ReadAliasFile('doc/git-mailrc')
276    config = _ProjectConfigParser(project_name)
277    if config_fname == '':
278        config_fname = '%s/.patman' % os.getenv('HOME')
279
280    if not os.path.exists(config_fname):
281        print("No config file found ~/.patman\nCreating one...\n")
282        CreatePatmanConfigFile(config_fname)
283
284    config.read(config_fname)
285
286    for name, value in config.items('alias'):
287        alias[name] = value.split(',')
288
289    _UpdateDefaults(parser, config)
290
291# These are the aliases we understand, indexed by alias. Each member is a list.
292alias = {}
293
294if __name__ == "__main__":
295    import doctest
296
297    doctest.testmod()
298