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") 61a1dcee84SDoug Anderson '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") 67a1dcee84SDoug Anderson '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")) 73a1dcee84SDoug Anderson [('am_hero', 'True'), ('process_tags', '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")) 79a1dcee84SDoug Anderson [('am_hero', '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 102a1dcee84SDoug Anderson def get(self, section, option, *args, **kwargs): 103a1dcee84SDoug Anderson """Extend SafeConfigParser to try project_section before section. 104a1dcee84SDoug Anderson 105a1dcee84SDoug Anderson Args: 106a1dcee84SDoug Anderson See SafeConfigParser. 107a1dcee84SDoug Anderson Returns: 108a1dcee84SDoug Anderson See SafeConfigParser. 109a1dcee84SDoug Anderson """ 110a1dcee84SDoug Anderson try: 111a1dcee84SDoug Anderson return ConfigParser.SafeConfigParser.get( 112a1dcee84SDoug Anderson self, "%s_%s" % (self._project_name, section), option, 113a1dcee84SDoug Anderson *args, **kwargs 114a1dcee84SDoug Anderson ) 115a1dcee84SDoug Anderson except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): 116a1dcee84SDoug Anderson return ConfigParser.SafeConfigParser.get( 117a1dcee84SDoug Anderson self, section, option, *args, **kwargs 118a1dcee84SDoug Anderson ) 119a1dcee84SDoug Anderson 120a1dcee84SDoug Anderson def items(self, section, *args, **kwargs): 121a1dcee84SDoug Anderson """Extend SafeConfigParser to add project_section to section. 122a1dcee84SDoug Anderson 123a1dcee84SDoug Anderson Args: 124a1dcee84SDoug Anderson See SafeConfigParser. 125a1dcee84SDoug Anderson Returns: 126a1dcee84SDoug Anderson See SafeConfigParser. 127a1dcee84SDoug Anderson """ 128a1dcee84SDoug Anderson project_items = [] 129a1dcee84SDoug Anderson has_project_section = False 130a1dcee84SDoug Anderson top_items = [] 131a1dcee84SDoug Anderson 132a1dcee84SDoug Anderson # Get items from the project section 133a1dcee84SDoug Anderson try: 134a1dcee84SDoug Anderson project_items = ConfigParser.SafeConfigParser.items( 135a1dcee84SDoug Anderson self, "%s_%s" % (self._project_name, section), *args, **kwargs 136a1dcee84SDoug Anderson ) 137a1dcee84SDoug Anderson has_project_section = True 138a1dcee84SDoug Anderson except ConfigParser.NoSectionError: 139a1dcee84SDoug Anderson pass 140a1dcee84SDoug Anderson 141a1dcee84SDoug Anderson # Get top-level items 142a1dcee84SDoug Anderson try: 143a1dcee84SDoug Anderson top_items = ConfigParser.SafeConfigParser.items( 144a1dcee84SDoug Anderson self, section, *args, **kwargs 145a1dcee84SDoug Anderson ) 146a1dcee84SDoug Anderson except ConfigParser.NoSectionError: 147a1dcee84SDoug Anderson # If neither section exists raise the error on... 148a1dcee84SDoug Anderson if not has_project_section: 149a1dcee84SDoug Anderson raise 150a1dcee84SDoug Anderson 151a1dcee84SDoug Anderson item_dict = dict(top_items) 152a1dcee84SDoug Anderson item_dict.update(project_items) 153a1dcee84SDoug Anderson return item_dict.items() 154a1dcee84SDoug Anderson 1550d24de9dSSimon Glassdef ReadGitAliases(fname): 1560d24de9dSSimon Glass """Read a git alias file. This is in the form used by git: 1570d24de9dSSimon Glass 1580d24de9dSSimon Glass alias uboot u-boot@lists.denx.de 1590d24de9dSSimon Glass alias wd Wolfgang Denk <wd@denx.de> 1600d24de9dSSimon Glass 1610d24de9dSSimon Glass Args: 1620d24de9dSSimon Glass fname: Filename to read 1630d24de9dSSimon Glass """ 1640d24de9dSSimon Glass try: 1650d24de9dSSimon Glass fd = open(fname, 'r') 1660d24de9dSSimon Glass except IOError: 167a920a17bSPaul Burton print("Warning: Cannot find alias file '%s'" % fname) 1680d24de9dSSimon Glass return 1690d24de9dSSimon Glass 1700d24de9dSSimon Glass re_line = re.compile('alias\s+(\S+)\s+(.*)') 1710d24de9dSSimon Glass for line in fd.readlines(): 1720d24de9dSSimon Glass line = line.strip() 1730d24de9dSSimon Glass if not line or line[0] == '#': 1740d24de9dSSimon Glass continue 1750d24de9dSSimon Glass 1760d24de9dSSimon Glass m = re_line.match(line) 1770d24de9dSSimon Glass if not m: 178a920a17bSPaul Burton print("Warning: Alias file line '%s' not understood" % line) 1790d24de9dSSimon Glass continue 1800d24de9dSSimon Glass 1810d24de9dSSimon Glass list = alias.get(m.group(1), []) 1820d24de9dSSimon Glass for item in m.group(2).split(','): 1830d24de9dSSimon Glass item = item.strip() 1840d24de9dSSimon Glass if item: 1850d24de9dSSimon Glass list.append(item) 1860d24de9dSSimon Glass alias[m.group(1)] = list 1870d24de9dSSimon Glass 1880d24de9dSSimon Glass fd.close() 1890d24de9dSSimon Glass 19087d65558SVikram Narayanandef CreatePatmanConfigFile(config_fname): 19187d65558SVikram Narayanan """Creates a config file under $(HOME)/.patman if it can't find one. 19287d65558SVikram Narayanan 19387d65558SVikram Narayanan Args: 19487d65558SVikram Narayanan config_fname: Default config filename i.e., $(HOME)/.patman 19587d65558SVikram Narayanan 19687d65558SVikram Narayanan Returns: 19787d65558SVikram Narayanan None 19887d65558SVikram Narayanan """ 19987d65558SVikram Narayanan name = gitutil.GetDefaultUserName() 20087d65558SVikram Narayanan if name == None: 20187d65558SVikram Narayanan name = raw_input("Enter name: ") 20287d65558SVikram Narayanan 20387d65558SVikram Narayanan email = gitutil.GetDefaultUserEmail() 20487d65558SVikram Narayanan 20587d65558SVikram Narayanan if email == None: 20687d65558SVikram Narayanan email = raw_input("Enter email: ") 20787d65558SVikram Narayanan 20887d65558SVikram Narayanan try: 20987d65558SVikram Narayanan f = open(config_fname, 'w') 21087d65558SVikram Narayanan except IOError: 211a920a17bSPaul Burton print("Couldn't create patman config file\n") 21287d65558SVikram Narayanan raise 21387d65558SVikram Narayanan 214ad893140SSimon Glass print('''[alias] 215ad893140SSimon Glassme: %s <%s> 216ad893140SSimon Glass 217ad893140SSimon Glass[bounces] 218ad893140SSimon Glassnxp = Zhikang Zhang <zhikang.zhang@nxp.com> 219ad893140SSimon Glass''' % (name, email), file=f) 22087d65558SVikram Narayanan f.close(); 22187d65558SVikram Narayanan 2228568baedSDoug Andersondef _UpdateDefaults(parser, config): 2238568baedSDoug Anderson """Update the given OptionParser defaults based on config. 2248568baedSDoug Anderson 2258568baedSDoug Anderson We'll walk through all of the settings from the parser 2268568baedSDoug Anderson For each setting we'll look for a default in the option parser. 2278568baedSDoug Anderson If it's found we'll update the option parser default. 2288568baedSDoug Anderson 2298568baedSDoug Anderson The idea here is that the .patman file should be able to update 2308568baedSDoug Anderson defaults but that command line flags should still have the final 2318568baedSDoug Anderson say. 2328568baedSDoug Anderson 2338568baedSDoug Anderson Args: 2348568baedSDoug Anderson parser: An instance of an OptionParser whose defaults will be 2358568baedSDoug Anderson updated. 236a1dcee84SDoug Anderson config: An instance of _ProjectConfigParser that we will query 2378568baedSDoug Anderson for settings. 2388568baedSDoug Anderson """ 2398568baedSDoug Anderson defaults = parser.get_default_values() 2408568baedSDoug Anderson for name, val in config.items('settings'): 2418568baedSDoug Anderson if hasattr(defaults, name): 2428568baedSDoug Anderson default_val = getattr(defaults, name) 2438568baedSDoug Anderson if isinstance(default_val, bool): 2448568baedSDoug Anderson val = config.getboolean('settings', name) 2458568baedSDoug Anderson elif isinstance(default_val, int): 2468568baedSDoug Anderson val = config.getint('settings', name) 2478568baedSDoug Anderson parser.set_default(name, val) 2488568baedSDoug Anderson else: 249a920a17bSPaul Burton print("WARNING: Unknown setting %s" % name) 2508568baedSDoug Anderson 2518895b3e1SSimon Glassdef _ReadAliasFile(fname): 2528895b3e1SSimon Glass """Read in the U-Boot git alias file if it exists. 2538895b3e1SSimon Glass 2548895b3e1SSimon Glass Args: 2558895b3e1SSimon Glass fname: Filename to read. 2568895b3e1SSimon Glass """ 2578895b3e1SSimon Glass if os.path.exists(fname): 2588895b3e1SSimon Glass bad_line = None 2598895b3e1SSimon Glass with open(fname) as fd: 2608895b3e1SSimon Glass linenum = 0 2618895b3e1SSimon Glass for line in fd: 2628895b3e1SSimon Glass linenum += 1 2638895b3e1SSimon Glass line = line.strip() 2648895b3e1SSimon Glass if not line or line.startswith('#'): 2658895b3e1SSimon Glass continue 266*b8a48fbcSAdam Sampson words = line.split(None, 2) 2678895b3e1SSimon Glass if len(words) < 3 or words[0] != 'alias': 2688895b3e1SSimon Glass if not bad_line: 2698895b3e1SSimon Glass bad_line = "%s:%d:Invalid line '%s'" % (fname, linenum, 2708895b3e1SSimon Glass line) 2718895b3e1SSimon Glass continue 2728895b3e1SSimon Glass alias[words[1]] = [s.strip() for s in words[2].split(',')] 2738895b3e1SSimon Glass if bad_line: 274a920a17bSPaul Burton print(bad_line) 2758895b3e1SSimon Glass 276e11aa602SChris Packhamdef _ReadBouncesFile(fname): 277e11aa602SChris Packham """Read in the bounces file if it exists 278e11aa602SChris Packham 279e11aa602SChris Packham Args: 280e11aa602SChris Packham fname: Filename to read. 281e11aa602SChris Packham """ 282e11aa602SChris Packham if os.path.exists(fname): 283e11aa602SChris Packham with open(fname) as fd: 284e11aa602SChris Packham for line in fd: 285e11aa602SChris Packham if line.startswith('#'): 286e11aa602SChris Packham continue 287e11aa602SChris Packham bounces.add(line.strip()) 288e11aa602SChris Packham 289ad893140SSimon Glassdef GetItems(config, section): 290ad893140SSimon Glass """Get the items from a section of the config. 291ad893140SSimon Glass 292ad893140SSimon Glass Args: 293ad893140SSimon Glass config: _ProjectConfigParser object containing settings 294ad893140SSimon Glass section: name of section to retrieve 295ad893140SSimon Glass 296ad893140SSimon Glass Returns: 297ad893140SSimon Glass List of (name, value) tuples for the section 298ad893140SSimon Glass """ 299ad893140SSimon Glass try: 300ad893140SSimon Glass return config.items(section) 301ad893140SSimon Glass except ConfigParser.NoSectionError as e: 302ad893140SSimon Glass return [] 303ad893140SSimon Glass except: 304ad893140SSimon Glass raise 305ad893140SSimon Glass 306a1dcee84SDoug Andersondef Setup(parser, project_name, config_fname=''): 3070d24de9dSSimon Glass """Set up the settings module by reading config files. 3080d24de9dSSimon Glass 3090d24de9dSSimon Glass Args: 3108568baedSDoug Anderson parser: The parser to update 311a1dcee84SDoug Anderson project_name: Name of project that we're working on; we'll look 312a1dcee84SDoug Anderson for sections named "project_section" as well. 3130d24de9dSSimon Glass config_fname: Config filename to read ('' for default) 3140d24de9dSSimon Glass """ 3158895b3e1SSimon Glass # First read the git alias file if available 3168895b3e1SSimon Glass _ReadAliasFile('doc/git-mailrc') 317a1dcee84SDoug Anderson config = _ProjectConfigParser(project_name) 3180d24de9dSSimon Glass if config_fname == '': 3192b36c75dSVikram Narayanan config_fname = '%s/.patman' % os.getenv('HOME') 32087d65558SVikram Narayanan 32187d65558SVikram Narayanan if not os.path.exists(config_fname): 322a920a17bSPaul Burton print("No config file found ~/.patman\nCreating one...\n") 32387d65558SVikram Narayanan CreatePatmanConfigFile(config_fname) 32487d65558SVikram Narayanan 3258568baedSDoug Anderson config.read(config_fname) 3260d24de9dSSimon Glass 327ad893140SSimon Glass for name, value in GetItems(config, 'alias'): 3280d24de9dSSimon Glass alias[name] = value.split(',') 3290d24de9dSSimon Glass 330e11aa602SChris Packham _ReadBouncesFile('doc/bounces') 331ad893140SSimon Glass for name, value in GetItems(config, 'bounces'): 332e11aa602SChris Packham bounces.add(value) 333e11aa602SChris Packham 3348568baedSDoug Anderson _UpdateDefaults(parser, config) 3350d24de9dSSimon Glass 3360d24de9dSSimon Glass# These are the aliases we understand, indexed by alias. Each member is a list. 3370d24de9dSSimon Glassalias = {} 338e11aa602SChris Packhambounces = set() 339a1dcee84SDoug Anderson 340a1dcee84SDoug Andersonif __name__ == "__main__": 341a1dcee84SDoug Anderson import doctest 342a1dcee84SDoug Anderson 343a1dcee84SDoug Anderson doctest.testmod() 344