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