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