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 82ce7b21eSPaul Burtontry: 92ce7b21eSPaul Burton import configparser as ConfigParser 102ce7b21eSPaul Burtonexcept: 110d24de9dSSimon Glass import ConfigParser 122ce7b21eSPaul 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... 39f5d44b9bSPaul Burton >>> try: 40f5d44b9bSPaul Burton ... from StringIO import StringIO 41f5d44b9bSPaul Burton ... except ImportError: 42f5d44b9bSPaul Burton ... from io import StringIO 43a1dcee84SDoug Anderson >>> sample_config = ''' 44a1dcee84SDoug Anderson ... [alias] 45a1dcee84SDoug Anderson ... me: Peter P. <likesspiders@example.com> 46a1dcee84SDoug Anderson ... enemies: Evil <evil@example.com> 47a1dcee84SDoug Anderson ... 48a1dcee84SDoug Anderson ... [sm_alias] 49a1dcee84SDoug Anderson ... enemies: Green G. <ugly@example.com> 50a1dcee84SDoug Anderson ... 51a1dcee84SDoug Anderson ... [sm2_alias] 52a1dcee84SDoug Anderson ... enemies: Doc O. <pus@example.com> 53a1dcee84SDoug Anderson ... 54a1dcee84SDoug Anderson ... [settings] 55a1dcee84SDoug Anderson ... am_hero: True 56a1dcee84SDoug Anderson ... ''' 57a1dcee84SDoug Anderson 58a1dcee84SDoug Anderson # Check to make sure that bogus project gets general alias. 59a1dcee84SDoug Anderson >>> config = _ProjectConfigParser("zzz") 60f5d44b9bSPaul Burton >>> config.readfp(StringIO(sample_config)) 61a1dcee84SDoug Anderson >>> config.get("alias", "enemies") 62a1dcee84SDoug Anderson 'Evil <evil@example.com>' 63a1dcee84SDoug Anderson 64a1dcee84SDoug Anderson # Check to make sure that alias gets overridden by project. 65a1dcee84SDoug Anderson >>> config = _ProjectConfigParser("sm") 66f5d44b9bSPaul Burton >>> config.readfp(StringIO(sample_config)) 67a1dcee84SDoug Anderson >>> config.get("alias", "enemies") 68a1dcee84SDoug Anderson 'Green G. <ugly@example.com>' 69a1dcee84SDoug Anderson 70a1dcee84SDoug Anderson # Check to make sure that settings get merged with project. 71a1dcee84SDoug Anderson >>> config = _ProjectConfigParser("linux") 72f5d44b9bSPaul Burton >>> config.readfp(StringIO(sample_config)) 73a1dcee84SDoug Anderson >>> sorted(config.items("settings")) 74a1dcee84SDoug Anderson [('am_hero', 'True'), ('process_tags', 'False')] 75a1dcee84SDoug Anderson 76a1dcee84SDoug Anderson # Check to make sure that settings works with unknown project. 77a1dcee84SDoug Anderson >>> config = _ProjectConfigParser("unknown") 78f5d44b9bSPaul Burton >>> config.readfp(StringIO(sample_config)) 79a1dcee84SDoug Anderson >>> sorted(config.items("settings")) 80a1dcee84SDoug Anderson [('am_hero', 'True')] 81a1dcee84SDoug Anderson """ 82a1dcee84SDoug Anderson def __init__(self, project_name): 83a1dcee84SDoug Anderson """Construct _ProjectConfigParser. 84a1dcee84SDoug Anderson 85a1dcee84SDoug Anderson In addition to standard SafeConfigParser initialization, this also loads 86a1dcee84SDoug Anderson project defaults. 87a1dcee84SDoug Anderson 88a1dcee84SDoug Anderson Args: 89a1dcee84SDoug Anderson project_name: The name of the project. 90a1dcee84SDoug Anderson """ 91a1dcee84SDoug Anderson self._project_name = project_name 92a1dcee84SDoug Anderson ConfigParser.SafeConfigParser.__init__(self) 93a1dcee84SDoug Anderson 94a1dcee84SDoug Anderson # Update the project settings in the config based on 95a1dcee84SDoug Anderson # the _default_settings global. 96a1dcee84SDoug Anderson project_settings = "%s_settings" % project_name 97a1dcee84SDoug Anderson if not self.has_section(project_settings): 98a1dcee84SDoug Anderson self.add_section(project_settings) 99a1dcee84SDoug Anderson project_defaults = _default_settings.get(project_name, {}) 100c9eac38aSPaul Burton for setting_name, setting_value in project_defaults.items(): 101a1dcee84SDoug Anderson self.set(project_settings, setting_name, setting_value) 102a1dcee84SDoug Anderson 103a1dcee84SDoug Anderson def get(self, section, option, *args, **kwargs): 104a1dcee84SDoug Anderson """Extend SafeConfigParser to try project_section before section. 105a1dcee84SDoug Anderson 106a1dcee84SDoug Anderson Args: 107a1dcee84SDoug Anderson See SafeConfigParser. 108a1dcee84SDoug Anderson Returns: 109a1dcee84SDoug Anderson See SafeConfigParser. 110a1dcee84SDoug Anderson """ 111a1dcee84SDoug Anderson try: 112a1dcee84SDoug Anderson return ConfigParser.SafeConfigParser.get( 113a1dcee84SDoug Anderson self, "%s_%s" % (self._project_name, section), option, 114a1dcee84SDoug Anderson *args, **kwargs 115a1dcee84SDoug Anderson ) 116a1dcee84SDoug Anderson except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): 117a1dcee84SDoug Anderson return ConfigParser.SafeConfigParser.get( 118a1dcee84SDoug Anderson self, section, option, *args, **kwargs 119a1dcee84SDoug Anderson ) 120a1dcee84SDoug Anderson 121a1dcee84SDoug Anderson def items(self, section, *args, **kwargs): 122a1dcee84SDoug Anderson """Extend SafeConfigParser to add project_section to section. 123a1dcee84SDoug Anderson 124a1dcee84SDoug Anderson Args: 125a1dcee84SDoug Anderson See SafeConfigParser. 126a1dcee84SDoug Anderson Returns: 127a1dcee84SDoug Anderson See SafeConfigParser. 128a1dcee84SDoug Anderson """ 129a1dcee84SDoug Anderson project_items = [] 130a1dcee84SDoug Anderson has_project_section = False 131a1dcee84SDoug Anderson top_items = [] 132a1dcee84SDoug Anderson 133a1dcee84SDoug Anderson # Get items from the project section 134a1dcee84SDoug Anderson try: 135a1dcee84SDoug Anderson project_items = ConfigParser.SafeConfigParser.items( 136a1dcee84SDoug Anderson self, "%s_%s" % (self._project_name, section), *args, **kwargs 137a1dcee84SDoug Anderson ) 138a1dcee84SDoug Anderson has_project_section = True 139a1dcee84SDoug Anderson except ConfigParser.NoSectionError: 140a1dcee84SDoug Anderson pass 141a1dcee84SDoug Anderson 142a1dcee84SDoug Anderson # Get top-level items 143a1dcee84SDoug Anderson try: 144a1dcee84SDoug Anderson top_items = ConfigParser.SafeConfigParser.items( 145a1dcee84SDoug Anderson self, section, *args, **kwargs 146a1dcee84SDoug Anderson ) 147a1dcee84SDoug Anderson except ConfigParser.NoSectionError: 148a1dcee84SDoug Anderson # If neither section exists raise the error on... 149a1dcee84SDoug Anderson if not has_project_section: 150a1dcee84SDoug Anderson raise 151a1dcee84SDoug Anderson 152a1dcee84SDoug Anderson item_dict = dict(top_items) 153a1dcee84SDoug Anderson item_dict.update(project_items) 154a1dcee84SDoug Anderson return item_dict.items() 155a1dcee84SDoug Anderson 1560d24de9dSSimon Glassdef ReadGitAliases(fname): 1570d24de9dSSimon Glass """Read a git alias file. This is in the form used by git: 1580d24de9dSSimon Glass 1590d24de9dSSimon Glass alias uboot u-boot@lists.denx.de 1600d24de9dSSimon Glass alias wd Wolfgang Denk <wd@denx.de> 1610d24de9dSSimon Glass 1620d24de9dSSimon Glass Args: 1630d24de9dSSimon Glass fname: Filename to read 1640d24de9dSSimon Glass """ 1650d24de9dSSimon Glass try: 1660d24de9dSSimon Glass fd = open(fname, 'r') 1670d24de9dSSimon Glass except IOError: 168a920a17bSPaul Burton print("Warning: Cannot find alias file '%s'" % fname) 1690d24de9dSSimon Glass return 1700d24de9dSSimon Glass 1710d24de9dSSimon Glass re_line = re.compile('alias\s+(\S+)\s+(.*)') 1720d24de9dSSimon Glass for line in fd.readlines(): 1730d24de9dSSimon Glass line = line.strip() 1740d24de9dSSimon Glass if not line or line[0] == '#': 1750d24de9dSSimon Glass continue 1760d24de9dSSimon Glass 1770d24de9dSSimon Glass m = re_line.match(line) 1780d24de9dSSimon Glass if not m: 179a920a17bSPaul Burton print("Warning: Alias file line '%s' not understood" % line) 1800d24de9dSSimon Glass continue 1810d24de9dSSimon Glass 1820d24de9dSSimon Glass list = alias.get(m.group(1), []) 1830d24de9dSSimon Glass for item in m.group(2).split(','): 1840d24de9dSSimon Glass item = item.strip() 1850d24de9dSSimon Glass if item: 1860d24de9dSSimon Glass list.append(item) 1870d24de9dSSimon Glass alias[m.group(1)] = list 1880d24de9dSSimon Glass 1890d24de9dSSimon Glass fd.close() 1900d24de9dSSimon Glass 19187d65558SVikram Narayanandef CreatePatmanConfigFile(config_fname): 19287d65558SVikram Narayanan """Creates a config file under $(HOME)/.patman if it can't find one. 19387d65558SVikram Narayanan 19487d65558SVikram Narayanan Args: 19587d65558SVikram Narayanan config_fname: Default config filename i.e., $(HOME)/.patman 19687d65558SVikram Narayanan 19787d65558SVikram Narayanan Returns: 19887d65558SVikram Narayanan None 19987d65558SVikram Narayanan """ 20087d65558SVikram Narayanan name = gitutil.GetDefaultUserName() 20187d65558SVikram Narayanan if name == None: 20287d65558SVikram Narayanan name = raw_input("Enter name: ") 20387d65558SVikram Narayanan 20487d65558SVikram Narayanan email = gitutil.GetDefaultUserEmail() 20587d65558SVikram Narayanan 20687d65558SVikram Narayanan if email == None: 20787d65558SVikram Narayanan email = raw_input("Enter email: ") 20887d65558SVikram Narayanan 20987d65558SVikram Narayanan try: 21087d65558SVikram Narayanan f = open(config_fname, 'w') 21187d65558SVikram Narayanan except IOError: 212a920a17bSPaul Burton print("Couldn't create patman config file\n") 21387d65558SVikram Narayanan raise 21487d65558SVikram Narayanan 215*d3aac30eSSimon Glass print('''[alias] 216*d3aac30eSSimon Glassme: %s <%s> 217*d3aac30eSSimon Glass 218*d3aac30eSSimon Glass[bounces] 219*d3aac30eSSimon Glassnxp = Zhikang Zhang <zhikang.zhang@nxp.com> 220*d3aac30eSSimon Glass''' % (name, email), file=f) 22187d65558SVikram Narayanan f.close(); 22287d65558SVikram Narayanan 2238568baedSDoug Andersondef _UpdateDefaults(parser, config): 2248568baedSDoug Anderson """Update the given OptionParser defaults based on config. 2258568baedSDoug Anderson 2268568baedSDoug Anderson We'll walk through all of the settings from the parser 2278568baedSDoug Anderson For each setting we'll look for a default in the option parser. 2288568baedSDoug Anderson If it's found we'll update the option parser default. 2298568baedSDoug Anderson 2308568baedSDoug Anderson The idea here is that the .patman file should be able to update 2318568baedSDoug Anderson defaults but that command line flags should still have the final 2328568baedSDoug Anderson say. 2338568baedSDoug Anderson 2348568baedSDoug Anderson Args: 2358568baedSDoug Anderson parser: An instance of an OptionParser whose defaults will be 2368568baedSDoug Anderson updated. 237a1dcee84SDoug Anderson config: An instance of _ProjectConfigParser that we will query 2388568baedSDoug Anderson for settings. 2398568baedSDoug Anderson """ 2408568baedSDoug Anderson defaults = parser.get_default_values() 2418568baedSDoug Anderson for name, val in config.items('settings'): 2428568baedSDoug Anderson if hasattr(defaults, name): 2438568baedSDoug Anderson default_val = getattr(defaults, name) 2448568baedSDoug Anderson if isinstance(default_val, bool): 2458568baedSDoug Anderson val = config.getboolean('settings', name) 2468568baedSDoug Anderson elif isinstance(default_val, int): 2478568baedSDoug Anderson val = config.getint('settings', name) 2488568baedSDoug Anderson parser.set_default(name, val) 2498568baedSDoug Anderson else: 250a920a17bSPaul Burton print("WARNING: Unknown setting %s" % name) 2518568baedSDoug Anderson 2528895b3e1SSimon Glassdef _ReadAliasFile(fname): 2538895b3e1SSimon Glass """Read in the U-Boot git alias file if it exists. 2548895b3e1SSimon Glass 2558895b3e1SSimon Glass Args: 2568895b3e1SSimon Glass fname: Filename to read. 2578895b3e1SSimon Glass """ 2588895b3e1SSimon Glass if os.path.exists(fname): 2598895b3e1SSimon Glass bad_line = None 2608895b3e1SSimon Glass with open(fname) as fd: 2618895b3e1SSimon Glass linenum = 0 2628895b3e1SSimon Glass for line in fd: 2638895b3e1SSimon Glass linenum += 1 2648895b3e1SSimon Glass line = line.strip() 2658895b3e1SSimon Glass if not line or line.startswith('#'): 2668895b3e1SSimon Glass continue 2678895b3e1SSimon Glass words = line.split(' ', 2) 2688895b3e1SSimon Glass if len(words) < 3 or words[0] != 'alias': 2698895b3e1SSimon Glass if not bad_line: 2708895b3e1SSimon Glass bad_line = "%s:%d:Invalid line '%s'" % (fname, linenum, 2718895b3e1SSimon Glass line) 2728895b3e1SSimon Glass continue 2738895b3e1SSimon Glass alias[words[1]] = [s.strip() for s in words[2].split(',')] 2748895b3e1SSimon Glass if bad_line: 275a920a17bSPaul Burton print(bad_line) 2768895b3e1SSimon Glass 277ad6e7aa6SChris Packhamdef _ReadBouncesFile(fname): 278ad6e7aa6SChris Packham """Read in the bounces file if it exists 279ad6e7aa6SChris Packham 280ad6e7aa6SChris Packham Args: 281ad6e7aa6SChris Packham fname: Filename to read. 282ad6e7aa6SChris Packham """ 283ad6e7aa6SChris Packham if os.path.exists(fname): 284ad6e7aa6SChris Packham with open(fname) as fd: 285ad6e7aa6SChris Packham for line in fd: 286ad6e7aa6SChris Packham if line.startswith('#'): 287ad6e7aa6SChris Packham continue 288ad6e7aa6SChris Packham bounces.add(line.strip()) 289ad6e7aa6SChris Packham 290*d3aac30eSSimon Glassdef GetItems(config, section): 291*d3aac30eSSimon Glass """Get the items from a section of the config. 292*d3aac30eSSimon Glass 293*d3aac30eSSimon Glass Args: 294*d3aac30eSSimon Glass config: _ProjectConfigParser object containing settings 295*d3aac30eSSimon Glass section: name of section to retrieve 296*d3aac30eSSimon Glass 297*d3aac30eSSimon Glass Returns: 298*d3aac30eSSimon Glass List of (name, value) tuples for the section 299*d3aac30eSSimon Glass """ 300*d3aac30eSSimon Glass try: 301*d3aac30eSSimon Glass return config.items(section) 302*d3aac30eSSimon Glass except ConfigParser.NoSectionError as e: 303*d3aac30eSSimon Glass return [] 304*d3aac30eSSimon Glass except: 305*d3aac30eSSimon Glass raise 306*d3aac30eSSimon Glass 307a1dcee84SDoug Andersondef Setup(parser, project_name, config_fname=''): 3080d24de9dSSimon Glass """Set up the settings module by reading config files. 3090d24de9dSSimon Glass 3100d24de9dSSimon Glass Args: 3118568baedSDoug Anderson parser: The parser to update 312a1dcee84SDoug Anderson project_name: Name of project that we're working on; we'll look 313a1dcee84SDoug Anderson for sections named "project_section" as well. 3140d24de9dSSimon Glass config_fname: Config filename to read ('' for default) 3150d24de9dSSimon Glass """ 3168895b3e1SSimon Glass # First read the git alias file if available 3178895b3e1SSimon Glass _ReadAliasFile('doc/git-mailrc') 318a1dcee84SDoug Anderson config = _ProjectConfigParser(project_name) 3190d24de9dSSimon Glass if config_fname == '': 3202b36c75dSVikram Narayanan config_fname = '%s/.patman' % os.getenv('HOME') 32187d65558SVikram Narayanan 32287d65558SVikram Narayanan if not os.path.exists(config_fname): 323a920a17bSPaul Burton print("No config file found ~/.patman\nCreating one...\n") 32487d65558SVikram Narayanan CreatePatmanConfigFile(config_fname) 32587d65558SVikram Narayanan 3268568baedSDoug Anderson config.read(config_fname) 3270d24de9dSSimon Glass 328*d3aac30eSSimon Glass for name, value in GetItems(config, 'alias'): 3290d24de9dSSimon Glass alias[name] = value.split(',') 3300d24de9dSSimon Glass 331ad6e7aa6SChris Packham _ReadBouncesFile('doc/bounces') 332*d3aac30eSSimon Glass for name, value in GetItems(config, 'bounces'): 333ad6e7aa6SChris Packham bounces.add(value) 334ad6e7aa6SChris Packham 3358568baedSDoug Anderson _UpdateDefaults(parser, config) 3360d24de9dSSimon Glass 3370d24de9dSSimon Glass# These are the aliases we understand, indexed by alias. Each member is a list. 3380d24de9dSSimon Glassalias = {} 339ad6e7aa6SChris Packhambounces = set() 340a1dcee84SDoug Anderson 341a1dcee84SDoug Andersonif __name__ == "__main__": 342a1dcee84SDoug Anderson import doctest 343a1dcee84SDoug Anderson 344a1dcee84SDoug Anderson doctest.testmod() 345