10d24de9dSSimon Glass# Copyright (c) 2011 The Chromium OS Authors. 20d24de9dSSimon Glass# 30d24de9dSSimon Glass# See file CREDITS for list of people who contributed to this 40d24de9dSSimon Glass# project. 50d24de9dSSimon Glass# 60d24de9dSSimon Glass# This program is free software; you can redistribute it and/or 70d24de9dSSimon Glass# modify it under the terms of the GNU General Public License as 80d24de9dSSimon Glass# published by the Free Software Foundation; either version 2 of 90d24de9dSSimon Glass# the License, or (at your option) any later version. 100d24de9dSSimon Glass# 110d24de9dSSimon Glass# This program is distributed in the hope that it will be useful, 120d24de9dSSimon Glass# but WITHOUT ANY WARRANTY; without even the implied warranty of 130d24de9dSSimon Glass# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 140d24de9dSSimon Glass# GNU General Public License for more details. 150d24de9dSSimon Glass# 160d24de9dSSimon Glass# You should have received a copy of the GNU General Public License 170d24de9dSSimon Glass# along with this program; if not, write to the Free Software 180d24de9dSSimon Glass# Foundation, Inc., 59 Temple Place, Suite 330, Boston, 190d24de9dSSimon Glass# MA 02111-1307 USA 200d24de9dSSimon Glass# 210d24de9dSSimon Glass 220d24de9dSSimon Glassimport ConfigParser 230d24de9dSSimon Glassimport os 240d24de9dSSimon Glassimport re 250d24de9dSSimon Glass 260d24de9dSSimon Glassimport command 2787d65558SVikram Narayananimport gitutil 280d24de9dSSimon Glass 29*a1dcee84SDoug Anderson"""Default settings per-project. 30*a1dcee84SDoug Anderson 31*a1dcee84SDoug AndersonThese are used by _ProjectConfigParser. Settings names should match 32*a1dcee84SDoug Andersonthe "dest" of the option parser from patman.py. 33*a1dcee84SDoug Anderson""" 34*a1dcee84SDoug Anderson_default_settings = { 35*a1dcee84SDoug Anderson "u-boot": {}, 36*a1dcee84SDoug Anderson "linux": { 37*a1dcee84SDoug Anderson "process_tags": "False", 38*a1dcee84SDoug Anderson } 39*a1dcee84SDoug Anderson} 40*a1dcee84SDoug Anderson 41*a1dcee84SDoug Andersonclass _ProjectConfigParser(ConfigParser.SafeConfigParser): 42*a1dcee84SDoug Anderson """ConfigParser that handles projects. 43*a1dcee84SDoug Anderson 44*a1dcee84SDoug Anderson There are two main goals of this class: 45*a1dcee84SDoug Anderson - Load project-specific default settings. 46*a1dcee84SDoug Anderson - Merge general default settings/aliases with project-specific ones. 47*a1dcee84SDoug Anderson 48*a1dcee84SDoug Anderson # Sample config used for tests below... 49*a1dcee84SDoug Anderson >>> import StringIO 50*a1dcee84SDoug Anderson >>> sample_config = ''' 51*a1dcee84SDoug Anderson ... [alias] 52*a1dcee84SDoug Anderson ... me: Peter P. <likesspiders@example.com> 53*a1dcee84SDoug Anderson ... enemies: Evil <evil@example.com> 54*a1dcee84SDoug Anderson ... 55*a1dcee84SDoug Anderson ... [sm_alias] 56*a1dcee84SDoug Anderson ... enemies: Green G. <ugly@example.com> 57*a1dcee84SDoug Anderson ... 58*a1dcee84SDoug Anderson ... [sm2_alias] 59*a1dcee84SDoug Anderson ... enemies: Doc O. <pus@example.com> 60*a1dcee84SDoug Anderson ... 61*a1dcee84SDoug Anderson ... [settings] 62*a1dcee84SDoug Anderson ... am_hero: True 63*a1dcee84SDoug Anderson ... ''' 64*a1dcee84SDoug Anderson 65*a1dcee84SDoug Anderson # Check to make sure that bogus project gets general alias. 66*a1dcee84SDoug Anderson >>> config = _ProjectConfigParser("zzz") 67*a1dcee84SDoug Anderson >>> config.readfp(StringIO.StringIO(sample_config)) 68*a1dcee84SDoug Anderson >>> config.get("alias", "enemies") 69*a1dcee84SDoug Anderson 'Evil <evil@example.com>' 70*a1dcee84SDoug Anderson 71*a1dcee84SDoug Anderson # Check to make sure that alias gets overridden by project. 72*a1dcee84SDoug Anderson >>> config = _ProjectConfigParser("sm") 73*a1dcee84SDoug Anderson >>> config.readfp(StringIO.StringIO(sample_config)) 74*a1dcee84SDoug Anderson >>> config.get("alias", "enemies") 75*a1dcee84SDoug Anderson 'Green G. <ugly@example.com>' 76*a1dcee84SDoug Anderson 77*a1dcee84SDoug Anderson # Check to make sure that settings get merged with project. 78*a1dcee84SDoug Anderson >>> config = _ProjectConfigParser("linux") 79*a1dcee84SDoug Anderson >>> config.readfp(StringIO.StringIO(sample_config)) 80*a1dcee84SDoug Anderson >>> sorted(config.items("settings")) 81*a1dcee84SDoug Anderson [('am_hero', 'True'), ('process_tags', 'False')] 82*a1dcee84SDoug Anderson 83*a1dcee84SDoug Anderson # Check to make sure that settings works with unknown project. 84*a1dcee84SDoug Anderson >>> config = _ProjectConfigParser("unknown") 85*a1dcee84SDoug Anderson >>> config.readfp(StringIO.StringIO(sample_config)) 86*a1dcee84SDoug Anderson >>> sorted(config.items("settings")) 87*a1dcee84SDoug Anderson [('am_hero', 'True')] 88*a1dcee84SDoug Anderson """ 89*a1dcee84SDoug Anderson def __init__(self, project_name): 90*a1dcee84SDoug Anderson """Construct _ProjectConfigParser. 91*a1dcee84SDoug Anderson 92*a1dcee84SDoug Anderson In addition to standard SafeConfigParser initialization, this also loads 93*a1dcee84SDoug Anderson project defaults. 94*a1dcee84SDoug Anderson 95*a1dcee84SDoug Anderson Args: 96*a1dcee84SDoug Anderson project_name: The name of the project. 97*a1dcee84SDoug Anderson """ 98*a1dcee84SDoug Anderson self._project_name = project_name 99*a1dcee84SDoug Anderson ConfigParser.SafeConfigParser.__init__(self) 100*a1dcee84SDoug Anderson 101*a1dcee84SDoug Anderson # Update the project settings in the config based on 102*a1dcee84SDoug Anderson # the _default_settings global. 103*a1dcee84SDoug Anderson project_settings = "%s_settings" % project_name 104*a1dcee84SDoug Anderson if not self.has_section(project_settings): 105*a1dcee84SDoug Anderson self.add_section(project_settings) 106*a1dcee84SDoug Anderson project_defaults = _default_settings.get(project_name, {}) 107*a1dcee84SDoug Anderson for setting_name, setting_value in project_defaults.iteritems(): 108*a1dcee84SDoug Anderson self.set(project_settings, setting_name, setting_value) 109*a1dcee84SDoug Anderson 110*a1dcee84SDoug Anderson def get(self, section, option, *args, **kwargs): 111*a1dcee84SDoug Anderson """Extend SafeConfigParser to try project_section before section. 112*a1dcee84SDoug Anderson 113*a1dcee84SDoug Anderson Args: 114*a1dcee84SDoug Anderson See SafeConfigParser. 115*a1dcee84SDoug Anderson Returns: 116*a1dcee84SDoug Anderson See SafeConfigParser. 117*a1dcee84SDoug Anderson """ 118*a1dcee84SDoug Anderson try: 119*a1dcee84SDoug Anderson return ConfigParser.SafeConfigParser.get( 120*a1dcee84SDoug Anderson self, "%s_%s" % (self._project_name, section), option, 121*a1dcee84SDoug Anderson *args, **kwargs 122*a1dcee84SDoug Anderson ) 123*a1dcee84SDoug Anderson except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): 124*a1dcee84SDoug Anderson return ConfigParser.SafeConfigParser.get( 125*a1dcee84SDoug Anderson self, section, option, *args, **kwargs 126*a1dcee84SDoug Anderson ) 127*a1dcee84SDoug Anderson 128*a1dcee84SDoug Anderson def items(self, section, *args, **kwargs): 129*a1dcee84SDoug Anderson """Extend SafeConfigParser to add project_section to section. 130*a1dcee84SDoug Anderson 131*a1dcee84SDoug Anderson Args: 132*a1dcee84SDoug Anderson See SafeConfigParser. 133*a1dcee84SDoug Anderson Returns: 134*a1dcee84SDoug Anderson See SafeConfigParser. 135*a1dcee84SDoug Anderson """ 136*a1dcee84SDoug Anderson project_items = [] 137*a1dcee84SDoug Anderson has_project_section = False 138*a1dcee84SDoug Anderson top_items = [] 139*a1dcee84SDoug Anderson 140*a1dcee84SDoug Anderson # Get items from the project section 141*a1dcee84SDoug Anderson try: 142*a1dcee84SDoug Anderson project_items = ConfigParser.SafeConfigParser.items( 143*a1dcee84SDoug Anderson self, "%s_%s" % (self._project_name, section), *args, **kwargs 144*a1dcee84SDoug Anderson ) 145*a1dcee84SDoug Anderson has_project_section = True 146*a1dcee84SDoug Anderson except ConfigParser.NoSectionError: 147*a1dcee84SDoug Anderson pass 148*a1dcee84SDoug Anderson 149*a1dcee84SDoug Anderson # Get top-level items 150*a1dcee84SDoug Anderson try: 151*a1dcee84SDoug Anderson top_items = ConfigParser.SafeConfigParser.items( 152*a1dcee84SDoug Anderson self, section, *args, **kwargs 153*a1dcee84SDoug Anderson ) 154*a1dcee84SDoug Anderson except ConfigParser.NoSectionError: 155*a1dcee84SDoug Anderson # If neither section exists raise the error on... 156*a1dcee84SDoug Anderson if not has_project_section: 157*a1dcee84SDoug Anderson raise 158*a1dcee84SDoug Anderson 159*a1dcee84SDoug Anderson item_dict = dict(top_items) 160*a1dcee84SDoug Anderson item_dict.update(project_items) 161*a1dcee84SDoug Anderson return item_dict.items() 162*a1dcee84SDoug Anderson 1630d24de9dSSimon Glassdef ReadGitAliases(fname): 1640d24de9dSSimon Glass """Read a git alias file. This is in the form used by git: 1650d24de9dSSimon Glass 1660d24de9dSSimon Glass alias uboot u-boot@lists.denx.de 1670d24de9dSSimon Glass alias wd Wolfgang Denk <wd@denx.de> 1680d24de9dSSimon Glass 1690d24de9dSSimon Glass Args: 1700d24de9dSSimon Glass fname: Filename to read 1710d24de9dSSimon Glass """ 1720d24de9dSSimon Glass try: 1730d24de9dSSimon Glass fd = open(fname, 'r') 1740d24de9dSSimon Glass except IOError: 1750d24de9dSSimon Glass print "Warning: Cannot find alias file '%s'" % fname 1760d24de9dSSimon Glass return 1770d24de9dSSimon Glass 1780d24de9dSSimon Glass re_line = re.compile('alias\s+(\S+)\s+(.*)') 1790d24de9dSSimon Glass for line in fd.readlines(): 1800d24de9dSSimon Glass line = line.strip() 1810d24de9dSSimon Glass if not line or line[0] == '#': 1820d24de9dSSimon Glass continue 1830d24de9dSSimon Glass 1840d24de9dSSimon Glass m = re_line.match(line) 1850d24de9dSSimon Glass if not m: 1860d24de9dSSimon Glass print "Warning: Alias file line '%s' not understood" % line 1870d24de9dSSimon Glass continue 1880d24de9dSSimon Glass 1890d24de9dSSimon Glass list = alias.get(m.group(1), []) 1900d24de9dSSimon Glass for item in m.group(2).split(','): 1910d24de9dSSimon Glass item = item.strip() 1920d24de9dSSimon Glass if item: 1930d24de9dSSimon Glass list.append(item) 1940d24de9dSSimon Glass alias[m.group(1)] = list 1950d24de9dSSimon Glass 1960d24de9dSSimon Glass fd.close() 1970d24de9dSSimon Glass 19887d65558SVikram Narayanandef CreatePatmanConfigFile(config_fname): 19987d65558SVikram Narayanan """Creates a config file under $(HOME)/.patman if it can't find one. 20087d65558SVikram Narayanan 20187d65558SVikram Narayanan Args: 20287d65558SVikram Narayanan config_fname: Default config filename i.e., $(HOME)/.patman 20387d65558SVikram Narayanan 20487d65558SVikram Narayanan Returns: 20587d65558SVikram Narayanan None 20687d65558SVikram Narayanan """ 20787d65558SVikram Narayanan name = gitutil.GetDefaultUserName() 20887d65558SVikram Narayanan if name == None: 20987d65558SVikram Narayanan name = raw_input("Enter name: ") 21087d65558SVikram Narayanan 21187d65558SVikram Narayanan email = gitutil.GetDefaultUserEmail() 21287d65558SVikram Narayanan 21387d65558SVikram Narayanan if email == None: 21487d65558SVikram Narayanan email = raw_input("Enter email: ") 21587d65558SVikram Narayanan 21687d65558SVikram Narayanan try: 21787d65558SVikram Narayanan f = open(config_fname, 'w') 21887d65558SVikram Narayanan except IOError: 21987d65558SVikram Narayanan print "Couldn't create patman config file\n" 22087d65558SVikram Narayanan raise 22187d65558SVikram Narayanan 22287d65558SVikram Narayanan print >>f, "[alias]\nme: %s <%s>" % (name, email) 22387d65558SVikram Narayanan f.close(); 22487d65558SVikram Narayanan 2258568baedSDoug Andersondef _UpdateDefaults(parser, config): 2268568baedSDoug Anderson """Update the given OptionParser defaults based on config. 2278568baedSDoug Anderson 2288568baedSDoug Anderson We'll walk through all of the settings from the parser 2298568baedSDoug Anderson For each setting we'll look for a default in the option parser. 2308568baedSDoug Anderson If it's found we'll update the option parser default. 2318568baedSDoug Anderson 2328568baedSDoug Anderson The idea here is that the .patman file should be able to update 2338568baedSDoug Anderson defaults but that command line flags should still have the final 2348568baedSDoug Anderson say. 2358568baedSDoug Anderson 2368568baedSDoug Anderson Args: 2378568baedSDoug Anderson parser: An instance of an OptionParser whose defaults will be 2388568baedSDoug Anderson updated. 239*a1dcee84SDoug Anderson config: An instance of _ProjectConfigParser that we will query 2408568baedSDoug Anderson for settings. 2418568baedSDoug Anderson """ 2428568baedSDoug Anderson defaults = parser.get_default_values() 2438568baedSDoug Anderson for name, val in config.items('settings'): 2448568baedSDoug Anderson if hasattr(defaults, name): 2458568baedSDoug Anderson default_val = getattr(defaults, name) 2468568baedSDoug Anderson if isinstance(default_val, bool): 2478568baedSDoug Anderson val = config.getboolean('settings', name) 2488568baedSDoug Anderson elif isinstance(default_val, int): 2498568baedSDoug Anderson val = config.getint('settings', name) 2508568baedSDoug Anderson parser.set_default(name, val) 2518568baedSDoug Anderson else: 2528568baedSDoug Anderson print "WARNING: Unknown setting %s" % name 2538568baedSDoug Anderson 254*a1dcee84SDoug Andersondef Setup(parser, project_name, config_fname=''): 2550d24de9dSSimon Glass """Set up the settings module by reading config files. 2560d24de9dSSimon Glass 2570d24de9dSSimon Glass Args: 2588568baedSDoug Anderson parser: The parser to update 259*a1dcee84SDoug Anderson project_name: Name of project that we're working on; we'll look 260*a1dcee84SDoug Anderson for sections named "project_section" as well. 2610d24de9dSSimon Glass config_fname: Config filename to read ('' for default) 2620d24de9dSSimon Glass """ 263*a1dcee84SDoug Anderson config = _ProjectConfigParser(project_name) 2640d24de9dSSimon Glass if config_fname == '': 2652b36c75dSVikram Narayanan config_fname = '%s/.patman' % os.getenv('HOME') 26687d65558SVikram Narayanan 26787d65558SVikram Narayanan if not os.path.exists(config_fname): 26887d65558SVikram Narayanan print "No config file found ~/.patman\nCreating one...\n" 26987d65558SVikram Narayanan CreatePatmanConfigFile(config_fname) 27087d65558SVikram Narayanan 2718568baedSDoug Anderson config.read(config_fname) 2720d24de9dSSimon Glass 2738568baedSDoug Anderson for name, value in config.items('alias'): 2740d24de9dSSimon Glass alias[name] = value.split(',') 2750d24de9dSSimon Glass 2768568baedSDoug Anderson _UpdateDefaults(parser, config) 2770d24de9dSSimon Glass 2780d24de9dSSimon Glass# These are the aliases we understand, indexed by alias. Each member is a list. 2790d24de9dSSimon Glassalias = {} 280*a1dcee84SDoug Anderson 281*a1dcee84SDoug Andersonif __name__ == "__main__": 282*a1dcee84SDoug Anderson import doctest 283*a1dcee84SDoug Anderson 284*a1dcee84SDoug Anderson doctest.testmod() 285