10d24de9dSSimon Glass# Copyright (c) 2011 The Chromium OS Authors. 20d24de9dSSimon Glass# 31a459660SWolfgang Denk# SPDX-License-Identifier: GPL-2.0+ 40d24de9dSSimon Glass# 50d24de9dSSimon Glass 6a920a17bSPaul Burtonfrom __future__ import print_function 7a920a17bSPaul Burton 8*2ce7b21eSPaul Burtontry: 9*2ce7b21eSPaul Burton import configparser as ConfigParser 10*2ce7b21eSPaul Burtonexcept: 110d24de9dSSimon Glass import ConfigParser 12*2ce7b21eSPaul Burton 130d24de9dSSimon Glassimport os 140d24de9dSSimon Glassimport re 150d24de9dSSimon Glass 160d24de9dSSimon Glassimport command 1787d65558SVikram Narayananimport gitutil 180d24de9dSSimon Glass 19a1dcee84SDoug Anderson"""Default settings per-project. 20a1dcee84SDoug Anderson 21a1dcee84SDoug AndersonThese are used by _ProjectConfigParser. Settings names should match 22a1dcee84SDoug Andersonthe "dest" of the option parser from patman.py. 23a1dcee84SDoug Anderson""" 24a1dcee84SDoug Anderson_default_settings = { 25a1dcee84SDoug Anderson "u-boot": {}, 26a1dcee84SDoug Anderson "linux": { 27a1dcee84SDoug Anderson "process_tags": "False", 28a1dcee84SDoug Anderson } 29a1dcee84SDoug Anderson} 30a1dcee84SDoug Anderson 31a1dcee84SDoug Andersonclass _ProjectConfigParser(ConfigParser.SafeConfigParser): 32a1dcee84SDoug Anderson """ConfigParser that handles projects. 33a1dcee84SDoug Anderson 34a1dcee84SDoug Anderson There are two main goals of this class: 35a1dcee84SDoug Anderson - Load project-specific default settings. 36a1dcee84SDoug Anderson - Merge general default settings/aliases with project-specific ones. 37a1dcee84SDoug Anderson 38a1dcee84SDoug Anderson # Sample config used for tests below... 39a1dcee84SDoug Anderson >>> import StringIO 40a1dcee84SDoug Anderson >>> sample_config = ''' 41a1dcee84SDoug Anderson ... [alias] 42a1dcee84SDoug Anderson ... me: Peter P. <likesspiders@example.com> 43a1dcee84SDoug Anderson ... enemies: Evil <evil@example.com> 44a1dcee84SDoug Anderson ... 45a1dcee84SDoug Anderson ... [sm_alias] 46a1dcee84SDoug Anderson ... enemies: Green G. <ugly@example.com> 47a1dcee84SDoug Anderson ... 48a1dcee84SDoug Anderson ... [sm2_alias] 49a1dcee84SDoug Anderson ... enemies: Doc O. <pus@example.com> 50a1dcee84SDoug Anderson ... 51a1dcee84SDoug Anderson ... [settings] 52a1dcee84SDoug Anderson ... am_hero: True 53a1dcee84SDoug Anderson ... ''' 54a1dcee84SDoug Anderson 55a1dcee84SDoug Anderson # Check to make sure that bogus project gets general alias. 56a1dcee84SDoug Anderson >>> config = _ProjectConfigParser("zzz") 57a1dcee84SDoug Anderson >>> config.readfp(StringIO.StringIO(sample_config)) 58a1dcee84SDoug Anderson >>> config.get("alias", "enemies") 59a1dcee84SDoug Anderson 'Evil <evil@example.com>' 60a1dcee84SDoug Anderson 61a1dcee84SDoug Anderson # Check to make sure that alias gets overridden by project. 62a1dcee84SDoug Anderson >>> config = _ProjectConfigParser("sm") 63a1dcee84SDoug Anderson >>> config.readfp(StringIO.StringIO(sample_config)) 64a1dcee84SDoug Anderson >>> config.get("alias", "enemies") 65a1dcee84SDoug Anderson 'Green G. <ugly@example.com>' 66a1dcee84SDoug Anderson 67a1dcee84SDoug Anderson # Check to make sure that settings get merged with project. 68a1dcee84SDoug Anderson >>> config = _ProjectConfigParser("linux") 69a1dcee84SDoug Anderson >>> config.readfp(StringIO.StringIO(sample_config)) 70a1dcee84SDoug Anderson >>> sorted(config.items("settings")) 71a1dcee84SDoug Anderson [('am_hero', 'True'), ('process_tags', 'False')] 72a1dcee84SDoug Anderson 73a1dcee84SDoug Anderson # Check to make sure that settings works with unknown project. 74a1dcee84SDoug Anderson >>> config = _ProjectConfigParser("unknown") 75a1dcee84SDoug Anderson >>> config.readfp(StringIO.StringIO(sample_config)) 76a1dcee84SDoug Anderson >>> sorted(config.items("settings")) 77a1dcee84SDoug Anderson [('am_hero', 'True')] 78a1dcee84SDoug Anderson """ 79a1dcee84SDoug Anderson def __init__(self, project_name): 80a1dcee84SDoug Anderson """Construct _ProjectConfigParser. 81a1dcee84SDoug Anderson 82a1dcee84SDoug Anderson In addition to standard SafeConfigParser initialization, this also loads 83a1dcee84SDoug Anderson project defaults. 84a1dcee84SDoug Anderson 85a1dcee84SDoug Anderson Args: 86a1dcee84SDoug Anderson project_name: The name of the project. 87a1dcee84SDoug Anderson """ 88a1dcee84SDoug Anderson self._project_name = project_name 89a1dcee84SDoug Anderson ConfigParser.SafeConfigParser.__init__(self) 90a1dcee84SDoug Anderson 91a1dcee84SDoug Anderson # Update the project settings in the config based on 92a1dcee84SDoug Anderson # the _default_settings global. 93a1dcee84SDoug Anderson project_settings = "%s_settings" % project_name 94a1dcee84SDoug Anderson if not self.has_section(project_settings): 95a1dcee84SDoug Anderson self.add_section(project_settings) 96a1dcee84SDoug Anderson project_defaults = _default_settings.get(project_name, {}) 97a1dcee84SDoug Anderson for setting_name, setting_value in project_defaults.iteritems(): 98a1dcee84SDoug Anderson self.set(project_settings, setting_name, setting_value) 99a1dcee84SDoug Anderson 100a1dcee84SDoug Anderson def get(self, section, option, *args, **kwargs): 101a1dcee84SDoug Anderson """Extend SafeConfigParser to try project_section before section. 102a1dcee84SDoug Anderson 103a1dcee84SDoug Anderson Args: 104a1dcee84SDoug Anderson See SafeConfigParser. 105a1dcee84SDoug Anderson Returns: 106a1dcee84SDoug Anderson See SafeConfigParser. 107a1dcee84SDoug Anderson """ 108a1dcee84SDoug Anderson try: 109a1dcee84SDoug Anderson return ConfigParser.SafeConfigParser.get( 110a1dcee84SDoug Anderson self, "%s_%s" % (self._project_name, section), option, 111a1dcee84SDoug Anderson *args, **kwargs 112a1dcee84SDoug Anderson ) 113a1dcee84SDoug Anderson except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): 114a1dcee84SDoug Anderson return ConfigParser.SafeConfigParser.get( 115a1dcee84SDoug Anderson self, section, option, *args, **kwargs 116a1dcee84SDoug Anderson ) 117a1dcee84SDoug Anderson 118a1dcee84SDoug Anderson def items(self, section, *args, **kwargs): 119a1dcee84SDoug Anderson """Extend SafeConfigParser to add project_section to section. 120a1dcee84SDoug Anderson 121a1dcee84SDoug Anderson Args: 122a1dcee84SDoug Anderson See SafeConfigParser. 123a1dcee84SDoug Anderson Returns: 124a1dcee84SDoug Anderson See SafeConfigParser. 125a1dcee84SDoug Anderson """ 126a1dcee84SDoug Anderson project_items = [] 127a1dcee84SDoug Anderson has_project_section = False 128a1dcee84SDoug Anderson top_items = [] 129a1dcee84SDoug Anderson 130a1dcee84SDoug Anderson # Get items from the project section 131a1dcee84SDoug Anderson try: 132a1dcee84SDoug Anderson project_items = ConfigParser.SafeConfigParser.items( 133a1dcee84SDoug Anderson self, "%s_%s" % (self._project_name, section), *args, **kwargs 134a1dcee84SDoug Anderson ) 135a1dcee84SDoug Anderson has_project_section = True 136a1dcee84SDoug Anderson except ConfigParser.NoSectionError: 137a1dcee84SDoug Anderson pass 138a1dcee84SDoug Anderson 139a1dcee84SDoug Anderson # Get top-level items 140a1dcee84SDoug Anderson try: 141a1dcee84SDoug Anderson top_items = ConfigParser.SafeConfigParser.items( 142a1dcee84SDoug Anderson self, section, *args, **kwargs 143a1dcee84SDoug Anderson ) 144a1dcee84SDoug Anderson except ConfigParser.NoSectionError: 145a1dcee84SDoug Anderson # If neither section exists raise the error on... 146a1dcee84SDoug Anderson if not has_project_section: 147a1dcee84SDoug Anderson raise 148a1dcee84SDoug Anderson 149a1dcee84SDoug Anderson item_dict = dict(top_items) 150a1dcee84SDoug Anderson item_dict.update(project_items) 151a1dcee84SDoug Anderson return item_dict.items() 152a1dcee84SDoug Anderson 1530d24de9dSSimon Glassdef ReadGitAliases(fname): 1540d24de9dSSimon Glass """Read a git alias file. This is in the form used by git: 1550d24de9dSSimon Glass 1560d24de9dSSimon Glass alias uboot u-boot@lists.denx.de 1570d24de9dSSimon Glass alias wd Wolfgang Denk <wd@denx.de> 1580d24de9dSSimon Glass 1590d24de9dSSimon Glass Args: 1600d24de9dSSimon Glass fname: Filename to read 1610d24de9dSSimon Glass """ 1620d24de9dSSimon Glass try: 1630d24de9dSSimon Glass fd = open(fname, 'r') 1640d24de9dSSimon Glass except IOError: 165a920a17bSPaul Burton print("Warning: Cannot find alias file '%s'" % fname) 1660d24de9dSSimon Glass return 1670d24de9dSSimon Glass 1680d24de9dSSimon Glass re_line = re.compile('alias\s+(\S+)\s+(.*)') 1690d24de9dSSimon Glass for line in fd.readlines(): 1700d24de9dSSimon Glass line = line.strip() 1710d24de9dSSimon Glass if not line or line[0] == '#': 1720d24de9dSSimon Glass continue 1730d24de9dSSimon Glass 1740d24de9dSSimon Glass m = re_line.match(line) 1750d24de9dSSimon Glass if not m: 176a920a17bSPaul Burton print("Warning: Alias file line '%s' not understood" % line) 1770d24de9dSSimon Glass continue 1780d24de9dSSimon Glass 1790d24de9dSSimon Glass list = alias.get(m.group(1), []) 1800d24de9dSSimon Glass for item in m.group(2).split(','): 1810d24de9dSSimon Glass item = item.strip() 1820d24de9dSSimon Glass if item: 1830d24de9dSSimon Glass list.append(item) 1840d24de9dSSimon Glass alias[m.group(1)] = list 1850d24de9dSSimon Glass 1860d24de9dSSimon Glass fd.close() 1870d24de9dSSimon Glass 18887d65558SVikram Narayanandef CreatePatmanConfigFile(config_fname): 18987d65558SVikram Narayanan """Creates a config file under $(HOME)/.patman if it can't find one. 19087d65558SVikram Narayanan 19187d65558SVikram Narayanan Args: 19287d65558SVikram Narayanan config_fname: Default config filename i.e., $(HOME)/.patman 19387d65558SVikram Narayanan 19487d65558SVikram Narayanan Returns: 19587d65558SVikram Narayanan None 19687d65558SVikram Narayanan """ 19787d65558SVikram Narayanan name = gitutil.GetDefaultUserName() 19887d65558SVikram Narayanan if name == None: 19987d65558SVikram Narayanan name = raw_input("Enter name: ") 20087d65558SVikram Narayanan 20187d65558SVikram Narayanan email = gitutil.GetDefaultUserEmail() 20287d65558SVikram Narayanan 20387d65558SVikram Narayanan if email == None: 20487d65558SVikram Narayanan email = raw_input("Enter email: ") 20587d65558SVikram Narayanan 20687d65558SVikram Narayanan try: 20787d65558SVikram Narayanan f = open(config_fname, 'w') 20887d65558SVikram Narayanan except IOError: 209a920a17bSPaul Burton print("Couldn't create patman config file\n") 21087d65558SVikram Narayanan raise 21187d65558SVikram Narayanan 212a920a17bSPaul Burton print("[alias]\nme: %s <%s>" % (name, email), file=f) 21387d65558SVikram Narayanan f.close(); 21487d65558SVikram Narayanan 2158568baedSDoug Andersondef _UpdateDefaults(parser, config): 2168568baedSDoug Anderson """Update the given OptionParser defaults based on config. 2178568baedSDoug Anderson 2188568baedSDoug Anderson We'll walk through all of the settings from the parser 2198568baedSDoug Anderson For each setting we'll look for a default in the option parser. 2208568baedSDoug Anderson If it's found we'll update the option parser default. 2218568baedSDoug Anderson 2228568baedSDoug Anderson The idea here is that the .patman file should be able to update 2238568baedSDoug Anderson defaults but that command line flags should still have the final 2248568baedSDoug Anderson say. 2258568baedSDoug Anderson 2268568baedSDoug Anderson Args: 2278568baedSDoug Anderson parser: An instance of an OptionParser whose defaults will be 2288568baedSDoug Anderson updated. 229a1dcee84SDoug Anderson config: An instance of _ProjectConfigParser that we will query 2308568baedSDoug Anderson for settings. 2318568baedSDoug Anderson """ 2328568baedSDoug Anderson defaults = parser.get_default_values() 2338568baedSDoug Anderson for name, val in config.items('settings'): 2348568baedSDoug Anderson if hasattr(defaults, name): 2358568baedSDoug Anderson default_val = getattr(defaults, name) 2368568baedSDoug Anderson if isinstance(default_val, bool): 2378568baedSDoug Anderson val = config.getboolean('settings', name) 2388568baedSDoug Anderson elif isinstance(default_val, int): 2398568baedSDoug Anderson val = config.getint('settings', name) 2408568baedSDoug Anderson parser.set_default(name, val) 2418568baedSDoug Anderson else: 242a920a17bSPaul Burton print("WARNING: Unknown setting %s" % name) 2438568baedSDoug Anderson 2448895b3e1SSimon Glassdef _ReadAliasFile(fname): 2458895b3e1SSimon Glass """Read in the U-Boot git alias file if it exists. 2468895b3e1SSimon Glass 2478895b3e1SSimon Glass Args: 2488895b3e1SSimon Glass fname: Filename to read. 2498895b3e1SSimon Glass """ 2508895b3e1SSimon Glass if os.path.exists(fname): 2518895b3e1SSimon Glass bad_line = None 2528895b3e1SSimon Glass with open(fname) as fd: 2538895b3e1SSimon Glass linenum = 0 2548895b3e1SSimon Glass for line in fd: 2558895b3e1SSimon Glass linenum += 1 2568895b3e1SSimon Glass line = line.strip() 2578895b3e1SSimon Glass if not line or line.startswith('#'): 2588895b3e1SSimon Glass continue 2598895b3e1SSimon Glass words = line.split(' ', 2) 2608895b3e1SSimon Glass if len(words) < 3 or words[0] != 'alias': 2618895b3e1SSimon Glass if not bad_line: 2628895b3e1SSimon Glass bad_line = "%s:%d:Invalid line '%s'" % (fname, linenum, 2638895b3e1SSimon Glass line) 2648895b3e1SSimon Glass continue 2658895b3e1SSimon Glass alias[words[1]] = [s.strip() for s in words[2].split(',')] 2668895b3e1SSimon Glass if bad_line: 267a920a17bSPaul Burton print(bad_line) 2688895b3e1SSimon Glass 269a1dcee84SDoug Andersondef Setup(parser, project_name, config_fname=''): 2700d24de9dSSimon Glass """Set up the settings module by reading config files. 2710d24de9dSSimon Glass 2720d24de9dSSimon Glass Args: 2738568baedSDoug Anderson parser: The parser to update 274a1dcee84SDoug Anderson project_name: Name of project that we're working on; we'll look 275a1dcee84SDoug Anderson for sections named "project_section" as well. 2760d24de9dSSimon Glass config_fname: Config filename to read ('' for default) 2770d24de9dSSimon Glass """ 2788895b3e1SSimon Glass # First read the git alias file if available 2798895b3e1SSimon Glass _ReadAliasFile('doc/git-mailrc') 280a1dcee84SDoug Anderson config = _ProjectConfigParser(project_name) 2810d24de9dSSimon Glass if config_fname == '': 2822b36c75dSVikram Narayanan config_fname = '%s/.patman' % os.getenv('HOME') 28387d65558SVikram Narayanan 28487d65558SVikram Narayanan if not os.path.exists(config_fname): 285a920a17bSPaul Burton print("No config file found ~/.patman\nCreating one...\n") 28687d65558SVikram Narayanan CreatePatmanConfigFile(config_fname) 28787d65558SVikram Narayanan 2888568baedSDoug Anderson config.read(config_fname) 2890d24de9dSSimon Glass 2908568baedSDoug Anderson for name, value in config.items('alias'): 2910d24de9dSSimon Glass alias[name] = value.split(',') 2920d24de9dSSimon Glass 2938568baedSDoug Anderson _UpdateDefaults(parser, config) 2940d24de9dSSimon Glass 2950d24de9dSSimon Glass# These are the aliases we understand, indexed by alias. Each member is a list. 2960d24de9dSSimon Glassalias = {} 297a1dcee84SDoug Anderson 298a1dcee84SDoug Andersonif __name__ == "__main__": 299a1dcee84SDoug Anderson import doctest 300a1dcee84SDoug Anderson 301a1dcee84SDoug Anderson doctest.testmod() 302