xref: /openbmc/u-boot/tools/patman/settings.py (revision cf033e04)
11a459660SWolfgang Denk# SPDX-License-Identifier: GPL-2.0+
283d290c5STom Rini# Copyright (c) 2011 The Chromium OS Authors.
30d24de9dSSimon Glass#
40d24de9dSSimon Glass
5a920a17bSPaul Burtonfrom __future__ import print_function
6a920a17bSPaul Burton
72ce7b21eSPaul Burtontry:
82ce7b21eSPaul Burton    import configparser as ConfigParser
92ce7b21eSPaul Burtonexcept:
100d24de9dSSimon Glass    import ConfigParser
112ce7b21eSPaul Burton
120d24de9dSSimon Glassimport os
130d24de9dSSimon Glassimport re
140d24de9dSSimon Glass
150d24de9dSSimon Glassimport command
1687d65558SVikram Narayananimport gitutil
170d24de9dSSimon Glass
18a1dcee84SDoug Anderson"""Default settings per-project.
19a1dcee84SDoug Anderson
20a1dcee84SDoug AndersonThese are used by _ProjectConfigParser.  Settings names should match
21a1dcee84SDoug Andersonthe "dest" of the option parser from patman.py.
22a1dcee84SDoug Anderson"""
23a1dcee84SDoug Anderson_default_settings = {
24a1dcee84SDoug Anderson    "u-boot": {},
25a1dcee84SDoug Anderson    "linux": {
26a1dcee84SDoug Anderson        "process_tags": "False",
27a1dcee84SDoug Anderson    }
28a1dcee84SDoug Anderson}
29a1dcee84SDoug Anderson
30a1dcee84SDoug Andersonclass _ProjectConfigParser(ConfigParser.SafeConfigParser):
31a1dcee84SDoug Anderson    """ConfigParser that handles projects.
32a1dcee84SDoug Anderson
33a1dcee84SDoug Anderson    There are two main goals of this class:
34a1dcee84SDoug Anderson    - Load project-specific default settings.
35a1dcee84SDoug Anderson    - Merge general default settings/aliases with project-specific ones.
36a1dcee84SDoug Anderson
37a1dcee84SDoug Anderson    # Sample config used for tests below...
38f5d44b9bSPaul Burton    >>> try:
39f5d44b9bSPaul Burton    ...     from StringIO import StringIO
40f5d44b9bSPaul Burton    ... except ImportError:
41f5d44b9bSPaul Burton    ...     from io import StringIO
42a1dcee84SDoug Anderson    >>> sample_config = '''
43a1dcee84SDoug Anderson    ... [alias]
44a1dcee84SDoug Anderson    ... me: Peter P. <likesspiders@example.com>
45a1dcee84SDoug Anderson    ... enemies: Evil <evil@example.com>
46a1dcee84SDoug Anderson    ...
47a1dcee84SDoug Anderson    ... [sm_alias]
48a1dcee84SDoug Anderson    ... enemies: Green G. <ugly@example.com>
49a1dcee84SDoug Anderson    ...
50a1dcee84SDoug Anderson    ... [sm2_alias]
51a1dcee84SDoug Anderson    ... enemies: Doc O. <pus@example.com>
52a1dcee84SDoug Anderson    ...
53a1dcee84SDoug Anderson    ... [settings]
54a1dcee84SDoug Anderson    ... am_hero: True
55a1dcee84SDoug Anderson    ... '''
56a1dcee84SDoug Anderson
57a1dcee84SDoug Anderson    # Check to make sure that bogus project gets general alias.
58a1dcee84SDoug Anderson    >>> config = _ProjectConfigParser("zzz")
59f5d44b9bSPaul Burton    >>> config.readfp(StringIO(sample_config))
60a1dcee84SDoug Anderson    >>> config.get("alias", "enemies")
61*ec9e0f47SSimon Glass    u'Evil <evil@example.com>'
62a1dcee84SDoug Anderson
63a1dcee84SDoug Anderson    # Check to make sure that alias gets overridden by project.
64a1dcee84SDoug Anderson    >>> config = _ProjectConfigParser("sm")
65f5d44b9bSPaul Burton    >>> config.readfp(StringIO(sample_config))
66a1dcee84SDoug Anderson    >>> config.get("alias", "enemies")
67*ec9e0f47SSimon Glass    u'Green G. <ugly@example.com>'
68a1dcee84SDoug Anderson
69a1dcee84SDoug Anderson    # Check to make sure that settings get merged with project.
70a1dcee84SDoug Anderson    >>> config = _ProjectConfigParser("linux")
71f5d44b9bSPaul Burton    >>> config.readfp(StringIO(sample_config))
72a1dcee84SDoug Anderson    >>> sorted(config.items("settings"))
73*ec9e0f47SSimon Glass    [(u'am_hero', u'True'), (u'process_tags', u'False')]
74a1dcee84SDoug Anderson
75a1dcee84SDoug Anderson    # Check to make sure that settings works with unknown project.
76a1dcee84SDoug Anderson    >>> config = _ProjectConfigParser("unknown")
77f5d44b9bSPaul Burton    >>> config.readfp(StringIO(sample_config))
78a1dcee84SDoug Anderson    >>> sorted(config.items("settings"))
79*ec9e0f47SSimon Glass    [(u'am_hero', u'True')]
80a1dcee84SDoug Anderson    """
81a1dcee84SDoug Anderson    def __init__(self, project_name):
82a1dcee84SDoug Anderson        """Construct _ProjectConfigParser.
83a1dcee84SDoug Anderson
84a1dcee84SDoug Anderson        In addition to standard SafeConfigParser initialization, this also loads
85a1dcee84SDoug Anderson        project defaults.
86a1dcee84SDoug Anderson
87a1dcee84SDoug Anderson        Args:
88a1dcee84SDoug Anderson            project_name: The name of the project.
89a1dcee84SDoug Anderson        """
90a1dcee84SDoug Anderson        self._project_name = project_name
91a1dcee84SDoug Anderson        ConfigParser.SafeConfigParser.__init__(self)
92a1dcee84SDoug Anderson
93a1dcee84SDoug Anderson        # Update the project settings in the config based on
94a1dcee84SDoug Anderson        # the _default_settings global.
95a1dcee84SDoug Anderson        project_settings = "%s_settings" % project_name
96a1dcee84SDoug Anderson        if not self.has_section(project_settings):
97a1dcee84SDoug Anderson            self.add_section(project_settings)
98a1dcee84SDoug Anderson        project_defaults = _default_settings.get(project_name, {})
99c9eac38aSPaul Burton        for setting_name, setting_value in project_defaults.items():
100a1dcee84SDoug Anderson            self.set(project_settings, setting_name, setting_value)
101a1dcee84SDoug Anderson
102*ec9e0f47SSimon Glass    def _to_unicode(self, val):
103*ec9e0f47SSimon Glass        """Make sure a value is of type 'unicode'
104*ec9e0f47SSimon Glass
105*ec9e0f47SSimon Glass        Args:
106*ec9e0f47SSimon Glass            val: string or unicode object
107*ec9e0f47SSimon Glass
108*ec9e0f47SSimon Glass        Returns:
109*ec9e0f47SSimon Glass            unicode version of val
110*ec9e0f47SSimon Glass        """
111*ec9e0f47SSimon Glass        return val if isinstance(val, unicode) else val.decode('utf-8')
112*ec9e0f47SSimon Glass
113a1dcee84SDoug Anderson    def get(self, section, option, *args, **kwargs):
114a1dcee84SDoug Anderson        """Extend SafeConfigParser to try project_section before section.
115a1dcee84SDoug Anderson
116a1dcee84SDoug Anderson        Args:
117a1dcee84SDoug Anderson            See SafeConfigParser.
118a1dcee84SDoug Anderson        Returns:
119a1dcee84SDoug Anderson            See SafeConfigParser.
120a1dcee84SDoug Anderson        """
121a1dcee84SDoug Anderson        try:
122*ec9e0f47SSimon Glass            val = ConfigParser.SafeConfigParser.get(
123a1dcee84SDoug Anderson                self, "%s_%s" % (self._project_name, section), option,
124a1dcee84SDoug Anderson                *args, **kwargs
125a1dcee84SDoug Anderson            )
126a1dcee84SDoug Anderson        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
127*ec9e0f47SSimon Glass            val = ConfigParser.SafeConfigParser.get(
128a1dcee84SDoug Anderson                self, section, option, *args, **kwargs
129a1dcee84SDoug Anderson            )
130*ec9e0f47SSimon Glass        return self._to_unicode(val)
131a1dcee84SDoug Anderson
132a1dcee84SDoug Anderson    def items(self, section, *args, **kwargs):
133a1dcee84SDoug Anderson        """Extend SafeConfigParser to add project_section to section.
134a1dcee84SDoug Anderson
135a1dcee84SDoug Anderson        Args:
136a1dcee84SDoug Anderson            See SafeConfigParser.
137a1dcee84SDoug Anderson        Returns:
138a1dcee84SDoug Anderson            See SafeConfigParser.
139a1dcee84SDoug Anderson        """
140a1dcee84SDoug Anderson        project_items = []
141a1dcee84SDoug Anderson        has_project_section = False
142a1dcee84SDoug Anderson        top_items = []
143a1dcee84SDoug Anderson
144a1dcee84SDoug Anderson        # Get items from the project section
145a1dcee84SDoug Anderson        try:
146a1dcee84SDoug Anderson            project_items = ConfigParser.SafeConfigParser.items(
147a1dcee84SDoug Anderson                self, "%s_%s" % (self._project_name, section), *args, **kwargs
148a1dcee84SDoug Anderson            )
149a1dcee84SDoug Anderson            has_project_section = True
150a1dcee84SDoug Anderson        except ConfigParser.NoSectionError:
151a1dcee84SDoug Anderson            pass
152a1dcee84SDoug Anderson
153a1dcee84SDoug Anderson        # Get top-level items
154a1dcee84SDoug Anderson        try:
155a1dcee84SDoug Anderson            top_items = ConfigParser.SafeConfigParser.items(
156a1dcee84SDoug Anderson                self, section, *args, **kwargs
157a1dcee84SDoug Anderson            )
158a1dcee84SDoug Anderson        except ConfigParser.NoSectionError:
159a1dcee84SDoug Anderson            # If neither section exists raise the error on...
160a1dcee84SDoug Anderson            if not has_project_section:
161a1dcee84SDoug Anderson                raise
162a1dcee84SDoug Anderson
163a1dcee84SDoug Anderson        item_dict = dict(top_items)
164a1dcee84SDoug Anderson        item_dict.update(project_items)
165*ec9e0f47SSimon Glass        return {(self._to_unicode(item), self._to_unicode(val))
166*ec9e0f47SSimon Glass                for item, val in item_dict.iteritems()}
167a1dcee84SDoug Anderson
1680d24de9dSSimon Glassdef ReadGitAliases(fname):
1690d24de9dSSimon Glass    """Read a git alias file. This is in the form used by git:
1700d24de9dSSimon Glass
1710d24de9dSSimon Glass    alias uboot  u-boot@lists.denx.de
1720d24de9dSSimon Glass    alias wd     Wolfgang Denk <wd@denx.de>
1730d24de9dSSimon Glass
1740d24de9dSSimon Glass    Args:
1750d24de9dSSimon Glass        fname: Filename to read
1760d24de9dSSimon Glass    """
1770d24de9dSSimon Glass    try:
1780d24de9dSSimon Glass        fd = open(fname, 'r')
1790d24de9dSSimon Glass    except IOError:
180a920a17bSPaul Burton        print("Warning: Cannot find alias file '%s'" % fname)
1810d24de9dSSimon Glass        return
1820d24de9dSSimon Glass
1830d24de9dSSimon Glass    re_line = re.compile('alias\s+(\S+)\s+(.*)')
1840d24de9dSSimon Glass    for line in fd.readlines():
1850d24de9dSSimon Glass        line = line.strip()
1860d24de9dSSimon Glass        if not line or line[0] == '#':
1870d24de9dSSimon Glass            continue
1880d24de9dSSimon Glass
1890d24de9dSSimon Glass        m = re_line.match(line)
1900d24de9dSSimon Glass        if not m:
191a920a17bSPaul Burton            print("Warning: Alias file line '%s' not understood" % line)
1920d24de9dSSimon Glass            continue
1930d24de9dSSimon Glass
1940d24de9dSSimon Glass        list = alias.get(m.group(1), [])
1950d24de9dSSimon Glass        for item in m.group(2).split(','):
1960d24de9dSSimon Glass            item = item.strip()
1970d24de9dSSimon Glass            if item:
1980d24de9dSSimon Glass                list.append(item)
1990d24de9dSSimon Glass        alias[m.group(1)] = list
2000d24de9dSSimon Glass
2010d24de9dSSimon Glass    fd.close()
2020d24de9dSSimon Glass
20387d65558SVikram Narayanandef CreatePatmanConfigFile(config_fname):
20487d65558SVikram Narayanan    """Creates a config file under $(HOME)/.patman if it can't find one.
20587d65558SVikram Narayanan
20687d65558SVikram Narayanan    Args:
20787d65558SVikram Narayanan        config_fname: Default config filename i.e., $(HOME)/.patman
20887d65558SVikram Narayanan
20987d65558SVikram Narayanan    Returns:
21087d65558SVikram Narayanan        None
21187d65558SVikram Narayanan    """
21287d65558SVikram Narayanan    name = gitutil.GetDefaultUserName()
21387d65558SVikram Narayanan    if name == None:
21487d65558SVikram Narayanan        name = raw_input("Enter name: ")
21587d65558SVikram Narayanan
21687d65558SVikram Narayanan    email = gitutil.GetDefaultUserEmail()
21787d65558SVikram Narayanan
21887d65558SVikram Narayanan    if email == None:
21987d65558SVikram Narayanan        email = raw_input("Enter email: ")
22087d65558SVikram Narayanan
22187d65558SVikram Narayanan    try:
22287d65558SVikram Narayanan        f = open(config_fname, 'w')
22387d65558SVikram Narayanan    except IOError:
224a920a17bSPaul Burton        print("Couldn't create patman config file\n")
22587d65558SVikram Narayanan        raise
22687d65558SVikram Narayanan
227ad893140SSimon Glass    print('''[alias]
228ad893140SSimon Glassme: %s <%s>
229ad893140SSimon Glass
230ad893140SSimon Glass[bounces]
231ad893140SSimon Glassnxp = Zhikang Zhang <zhikang.zhang@nxp.com>
232ad893140SSimon Glass''' % (name, email), file=f)
23387d65558SVikram Narayanan    f.close();
23487d65558SVikram Narayanan
2358568baedSDoug Andersondef _UpdateDefaults(parser, config):
2368568baedSDoug Anderson    """Update the given OptionParser defaults based on config.
2378568baedSDoug Anderson
2388568baedSDoug Anderson    We'll walk through all of the settings from the parser
2398568baedSDoug Anderson    For each setting we'll look for a default in the option parser.
2408568baedSDoug Anderson    If it's found we'll update the option parser default.
2418568baedSDoug Anderson
2428568baedSDoug Anderson    The idea here is that the .patman file should be able to update
2438568baedSDoug Anderson    defaults but that command line flags should still have the final
2448568baedSDoug Anderson    say.
2458568baedSDoug Anderson
2468568baedSDoug Anderson    Args:
2478568baedSDoug Anderson        parser: An instance of an OptionParser whose defaults will be
2488568baedSDoug Anderson            updated.
249a1dcee84SDoug Anderson        config: An instance of _ProjectConfigParser that we will query
2508568baedSDoug Anderson            for settings.
2518568baedSDoug Anderson    """
2528568baedSDoug Anderson    defaults = parser.get_default_values()
2538568baedSDoug Anderson    for name, val in config.items('settings'):
2548568baedSDoug Anderson        if hasattr(defaults, name):
2558568baedSDoug Anderson            default_val = getattr(defaults, name)
2568568baedSDoug Anderson            if isinstance(default_val, bool):
2578568baedSDoug Anderson                val = config.getboolean('settings', name)
2588568baedSDoug Anderson            elif isinstance(default_val, int):
2598568baedSDoug Anderson                val = config.getint('settings', name)
2608568baedSDoug Anderson            parser.set_default(name, val)
2618568baedSDoug Anderson        else:
262a920a17bSPaul Burton            print("WARNING: Unknown setting %s" % name)
2638568baedSDoug Anderson
2648895b3e1SSimon Glassdef _ReadAliasFile(fname):
2658895b3e1SSimon Glass    """Read in the U-Boot git alias file if it exists.
2668895b3e1SSimon Glass
2678895b3e1SSimon Glass    Args:
2688895b3e1SSimon Glass        fname: Filename to read.
2698895b3e1SSimon Glass    """
2708895b3e1SSimon Glass    if os.path.exists(fname):
2718895b3e1SSimon Glass        bad_line = None
2728895b3e1SSimon Glass        with open(fname) as fd:
2738895b3e1SSimon Glass            linenum = 0
2748895b3e1SSimon Glass            for line in fd:
2758895b3e1SSimon Glass                linenum += 1
2768895b3e1SSimon Glass                line = line.strip()
2778895b3e1SSimon Glass                if not line or line.startswith('#'):
2788895b3e1SSimon Glass                    continue
279b8a48fbcSAdam Sampson                words = line.split(None, 2)
2808895b3e1SSimon Glass                if len(words) < 3 or words[0] != 'alias':
2818895b3e1SSimon Glass                    if not bad_line:
2828895b3e1SSimon Glass                        bad_line = "%s:%d:Invalid line '%s'" % (fname, linenum,
2838895b3e1SSimon Glass                                                                line)
2848895b3e1SSimon Glass                    continue
2858895b3e1SSimon Glass                alias[words[1]] = [s.strip() for s in words[2].split(',')]
2868895b3e1SSimon Glass        if bad_line:
287a920a17bSPaul Burton            print(bad_line)
2888895b3e1SSimon Glass
289e11aa602SChris Packhamdef _ReadBouncesFile(fname):
290e11aa602SChris Packham    """Read in the bounces file if it exists
291e11aa602SChris Packham
292e11aa602SChris Packham    Args:
293e11aa602SChris Packham        fname: Filename to read.
294e11aa602SChris Packham    """
295e11aa602SChris Packham    if os.path.exists(fname):
296e11aa602SChris Packham        with open(fname) as fd:
297e11aa602SChris Packham            for line in fd:
298e11aa602SChris Packham                if line.startswith('#'):
299e11aa602SChris Packham                    continue
300e11aa602SChris Packham                bounces.add(line.strip())
301e11aa602SChris Packham
302ad893140SSimon Glassdef GetItems(config, section):
303ad893140SSimon Glass    """Get the items from a section of the config.
304ad893140SSimon Glass
305ad893140SSimon Glass    Args:
306ad893140SSimon Glass        config: _ProjectConfigParser object containing settings
307ad893140SSimon Glass        section: name of section to retrieve
308ad893140SSimon Glass
309ad893140SSimon Glass    Returns:
310ad893140SSimon Glass        List of (name, value) tuples for the section
311ad893140SSimon Glass    """
312ad893140SSimon Glass    try:
313ad893140SSimon Glass        return config.items(section)
314ad893140SSimon Glass    except ConfigParser.NoSectionError as e:
315ad893140SSimon Glass        return []
316ad893140SSimon Glass    except:
317ad893140SSimon Glass        raise
318ad893140SSimon Glass
319a1dcee84SDoug Andersondef Setup(parser, project_name, config_fname=''):
3200d24de9dSSimon Glass    """Set up the settings module by reading config files.
3210d24de9dSSimon Glass
3220d24de9dSSimon Glass    Args:
3238568baedSDoug Anderson        parser:         The parser to update
324a1dcee84SDoug Anderson        project_name:   Name of project that we're working on; we'll look
325a1dcee84SDoug Anderson            for sections named "project_section" as well.
3260d24de9dSSimon Glass        config_fname:   Config filename to read ('' for default)
3270d24de9dSSimon Glass    """
3288895b3e1SSimon Glass    # First read the git alias file if available
3298895b3e1SSimon Glass    _ReadAliasFile('doc/git-mailrc')
330a1dcee84SDoug Anderson    config = _ProjectConfigParser(project_name)
3310d24de9dSSimon Glass    if config_fname == '':
3322b36c75dSVikram Narayanan        config_fname = '%s/.patman' % os.getenv('HOME')
33387d65558SVikram Narayanan
33487d65558SVikram Narayanan    if not os.path.exists(config_fname):
335a920a17bSPaul Burton        print("No config file found ~/.patman\nCreating one...\n")
33687d65558SVikram Narayanan        CreatePatmanConfigFile(config_fname)
33787d65558SVikram Narayanan
3388568baedSDoug Anderson    config.read(config_fname)
3390d24de9dSSimon Glass
340ad893140SSimon Glass    for name, value in GetItems(config, 'alias'):
3410d24de9dSSimon Glass        alias[name] = value.split(',')
3420d24de9dSSimon Glass
343e11aa602SChris Packham    _ReadBouncesFile('doc/bounces')
344ad893140SSimon Glass    for name, value in GetItems(config, 'bounces'):
345e11aa602SChris Packham        bounces.add(value)
346e11aa602SChris Packham
3478568baedSDoug Anderson    _UpdateDefaults(parser, config)
3480d24de9dSSimon Glass
3490d24de9dSSimon Glass# These are the aliases we understand, indexed by alias. Each member is a list.
3500d24de9dSSimon Glassalias = {}
351e11aa602SChris Packhambounces = set()
352a1dcee84SDoug Anderson
353a1dcee84SDoug Andersonif __name__ == "__main__":
354a1dcee84SDoug Anderson    import doctest
355a1dcee84SDoug Anderson
356a1dcee84SDoug Anderson    doctest.testmod()
357