[U-Boot] [PATCH 1/4] patman: Add a call to get_maintainer.pl if it exists

For Linux the best way to figure out where to send a patch is with the "get_maintainer.pl" script. Add support for calling it from patman. Support is added unconditionally for "scripts/get_maintainer.pl" in case it is helpful for any other projects.
Signed-off-by: Doug Anderson dianders@chromium.org --- tools/patman/README | 11 ++++++- tools/patman/get_maintainer.py | 63 ++++++++++++++++++++++++++++++++++++++++ tools/patman/series.py | 2 + 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 tools/patman/get_maintainer.py
diff --git a/tools/patman/README b/tools/patman/README index dc3957c..903d02f 100644 --- a/tools/patman/README +++ b/tools/patman/README @@ -43,6 +43,9 @@ Series-to: fred.blogs@napier.co.nz
in one of your commits, the series will be sent there.
+In Linux this will also call get_maintainer.pl on each of your +patches automatically. +
How to use this tool ==================== @@ -65,8 +68,12 @@ will get a consistent result each time. How to configure it ===================
-For most cases patman will locate and use the file 'doc/git-mailrc' in -your U-Boot directory. This contains most of the aliases you will need. +For most cases of using patman for U-Boot developement patman will +locate and use the file 'doc/git-mailrc' in your U-Boot directory. +This contains most of the aliases you will need. + +For Linux the 'scripts/get_maintainer.pl' handles figuring out where +to send patches pretty well.
During the first run patman creates a config file for you by taking the default user name and email address from the global .gitconfig file. diff --git a/tools/patman/get_maintainer.py b/tools/patman/get_maintainer.py new file mode 100644 index 0000000..cb11373 --- /dev/null +++ b/tools/patman/get_maintainer.py @@ -0,0 +1,63 @@ +# Copyright (c) 2012 The Chromium OS Authors. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +import command +import gitutil +import os + +def FindGetMaintainer(): + """Look for the get_maintainer.pl script. + + Returns: + If the script is found we'll return a path to it; else None. + """ + try_list = [ + os.path.join(gitutil.GetTopLevel(), 'scripts'), + ] + # Look in the list + for path in try_list: + fname = os.path.join(path, 'get_maintainer.pl') + if os.path.isfile(fname): + return fname + + return None + +def GetMaintainer(fname, verbose=False): + """Run get_maintainer.pl on a file if we find it. + + We look for get_maintainer.pl in the 'scripts' directory at the top of + git. If we find it we'll run it. If we don't find get_maintainer.pl + then we fail silently. + + Args: + fname: Path to the patch file to run get_maintainer.pl on. + + Returns: + A list of email addresses to CC to. + """ + get_maintainer = FindGetMaintainer() + if not get_maintainer: + if verbose: + print "WARNING: Couldn't find get_maintainer.pl" + return [] + + stdout = command.Output(get_maintainer, '--norolestats', fname) + return stdout.splitlines() diff --git a/tools/patman/series.py b/tools/patman/series.py index 083af0f..6c5c570 100644 --- a/tools/patman/series.py +++ b/tools/patman/series.py @@ -22,6 +22,7 @@ import itertools import os
+import get_maintainer import gitutil import terminal
@@ -225,6 +226,7 @@ class Series(dict): if process_tags: list += gitutil.BuildEmailList(commit.tags) list += gitutil.BuildEmailList(commit.cc_list) + list += get_maintainer.GetMaintainer(commit.patch) all_ccs += list print >>fd, commit.patch, ', '.join(list) self._generated_cc[commit.patch] = list

This patch adds support for a [settings] section in the .patman file. In this section you can add settings that will affect the default values for command-line options.
Support is added in a generic way such that any setting can be updated by just referring to the "dest" of the option that is passed to the option parser. At the moment options that would make sense to put in settings are "ignore_errors", "process_tags", and "verbose". You could override them like:
[settings] ignore_errors: True process_tags: False verbose: True
The settings functionality is also used in a future change which adds support for per-project settings.
Signed-off-by: Doug Anderson dianders@chromium.org --- tools/patman/README | 16 ++++++++++++++++ tools/patman/gitutil.py | 2 -- tools/patman/patman.py | 3 +++ tools/patman/settings.py | 39 +++++++++++++++++++++++++++++++++++---- 4 files changed, 54 insertions(+), 6 deletions(-)
diff --git a/tools/patman/README b/tools/patman/README index 903d02f..6ca5b5b 100644 --- a/tools/patman/README +++ b/tools/patman/README @@ -98,6 +98,22 @@ The checkpatch.pl in the U-Boot tools/ subdirectory will be located and used. Failing that you can put it into your path or ~/bin/checkpatch.pl
+If you want to change the defaults for patman's command-line arguments, +you can add a [settings] section to your .patman file. This can be used +for any command line option by referring to the "dest" for the option in +patman.py. For reference, the useful ones (at the moment) shown below +(all with the non-default setting): + +>>> + +[settings] +ignore_errors: True +process_tags: False +verbose: True + +<<< + + How to run it =============
diff --git a/tools/patman/gitutil.py b/tools/patman/gitutil.py index 72d37a0..e7753cf 100644 --- a/tools/patman/gitutil.py +++ b/tools/patman/gitutil.py @@ -377,8 +377,6 @@ def GetDefaultUserEmail():
def Setup(): """Set up git utils, by reading the alias files.""" - settings.Setup('') - # Check for a git alias file also alias_fname = GetAliasFile() if alias_fname: diff --git a/tools/patman/patman.py b/tools/patman/patman.py index 4181d80..b327c67 100755 --- a/tools/patman/patman.py +++ b/tools/patman/patman.py @@ -34,6 +34,7 @@ import checkpatch import command import gitutil import patchstream +import settings import terminal import test
@@ -64,6 +65,8 @@ parser.usage = """patman [options] Create patches from commits in a branch, check them and email them as specified by tags you place in the commits. Use -n to """
+ +settings.Setup(parser, '') (options, args) = parser.parse_args()
# Run our meagre tests diff --git a/tools/patman/settings.py b/tools/patman/settings.py index 4dda17b..5208f7d 100644 --- a/tools/patman/settings.py +++ b/tools/patman/settings.py @@ -88,13 +88,43 @@ def CreatePatmanConfigFile(config_fname): print >>f, "[alias]\nme: %s <%s>" % (name, email) f.close();
-def Setup(config_fname=''): +def _UpdateDefaults(parser, config): + """Update the given OptionParser defaults based on config. + + We'll walk through all of the settings from the parser + For each setting we'll look for a default in the option parser. + If it's found we'll update the option parser default. + + The idea here is that the .patman file should be able to update + defaults but that command line flags should still have the final + say. + + Args: + parser: An instance of an OptionParser whose defaults will be + updated. + config: An instance of SafeConfigParser that we will query + for settings. + """ + defaults = parser.get_default_values() + for name, val in config.items('settings'): + if hasattr(defaults, name): + default_val = getattr(defaults, name) + if isinstance(default_val, bool): + val = config.getboolean('settings', name) + elif isinstance(default_val, int): + val = config.getint('settings', name) + parser.set_default(name, val) + else: + print "WARNING: Unknown setting %s" % name + +def Setup(parser, config_fname=''): """Set up the settings module by reading config files.
Args: + parser: The parser to update config_fname: Config filename to read ('' for default) """ - settings = ConfigParser.SafeConfigParser() + config = ConfigParser.SafeConfigParser() if config_fname == '': config_fname = '%s/.patman' % os.getenv('HOME')
@@ -102,11 +132,12 @@ def Setup(config_fname=''): print "No config file found ~/.patman\nCreating one...\n" CreatePatmanConfigFile(config_fname)
- settings.read(config_fname) + config.read(config_fname)
- for name, value in settings.items('alias'): + for name, value in config.items('alias'): alias[name] = value.split(',')
+ _UpdateDefaults(parser, config)
# These are the aliases we understand, indexed by alias. Each member is a list. alias = {}

On Fri, Nov 30, 2012 at 4:29 PM, Doug Anderson dianders@chromium.org wrote:
This patch adds support for a [settings] section in the .patman file. In this section you can add settings that will affect the default values for command-line options.
Support is added in a generic way such that any setting can be updated by just referring to the "dest" of the option that is passed to the option parser. At the moment options that would make sense to put in settings are "ignore_errors", "process_tags", and "verbose". You could override them like:
[settings] ignore_errors: True process_tags: False verbose: True
The settings functionality is also used in a future change which adds support for per-project settings.
Signed-off-by: Doug Anderson dianders@chromium.org
Acked-by: Simon Glass sjg@chromium.org
tools/patman/README | 16 ++++++++++++++++ tools/patman/gitutil.py | 2 -- tools/patman/patman.py | 3 +++ tools/patman/settings.py | 39 +++++++++++++++++++++++++++++++++++---- 4 files changed, 54 insertions(+), 6 deletions(-)

There are cases that we want to support different settings (or maybe even different aliases) for different projects. Add support for this by: * Adding detection for two big projects: U-Boot and Linux. * Adding default settings for Linux (U-Boot is already good with the standard patman defaults). * Extend the new "settings" feature in .patman to specify per-project settings.
Signed-off-by: Doug Anderson dianders@chromium.org --- tools/patman/README | 13 ++++ tools/patman/patman.py | 9 +++- tools/patman/project.py | 43 +++++++++++++ tools/patman/settings.py | 147 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 tools/patman/project.py
diff --git a/tools/patman/README b/tools/patman/README index 6ca5b5b..d294f3d 100644 --- a/tools/patman/README +++ b/tools/patman/README @@ -114,6 +114,19 @@ verbose: True <<<
+If you want to adjust settings (or aliases) that affect just a single +project you can add a section that looks like [project_settings] or +[project_alias]. If you want to use tags for your linux work, you could +do: + +>>> + +[linux_settings] +process_tags: True + +<<< + + How to run it =============
diff --git a/tools/patman/patman.py b/tools/patman/patman.py index b327c67..54a252e 100755 --- a/tools/patman/patman.py +++ b/tools/patman/patman.py @@ -34,6 +34,7 @@ import checkpatch import command import gitutil import patchstream +import project import settings import terminal import test @@ -59,6 +60,9 @@ parser.add_option('--cc-cmd', dest='cc_cmd', type='string', action='store', default=None, help='Output cc list for patch file (used by git)') parser.add_option('--no-tags', action='store_false', dest='process_tags', default=True, help="Don't process subject tags as aliaes") +parser.add_option('--project', default=project.DetectProject(), + help="Project name; affects default option values and " + "aliases [default: %default]")
parser.usage = """patman [options]
@@ -66,7 +70,10 @@ Create patches from commits in a branch, check them and email them as specified by tags you place in the commits. Use -n to """
-settings.Setup(parser, '') +# Parse options twice: first to get the project and second to handle +# defaults properly (which depends on project). +(options, args) = parser.parse_args() +settings.Setup(parser, options.project, '') (options, args) = parser.parse_args()
# Run our meagre tests diff --git a/tools/patman/project.py b/tools/patman/project.py new file mode 100644 index 0000000..4f7b2b3 --- /dev/null +++ b/tools/patman/project.py @@ -0,0 +1,43 @@ +# Copyright (c) 2012 The Chromium OS Authors. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +import os.path + +import gitutil + +def DetectProject(): + """Autodetect the name of the current project. + + This looks for signature files/directories that are unlikely to exist except + in the given project. + + Returns: + The name of the project, like "linux" or "u-boot". Returns "unknown" + if we can't detect the project. + """ + top_level = gitutil.GetTopLevel() + + if os.path.exists(os.path.join(top_level, "include", "u-boot")): + return "u-boot" + elif os.path.exists(os.path.join(top_level, "kernel")): + return "linux" + + return "unknown" diff --git a/tools/patman/settings.py b/tools/patman/settings.py index 5208f7d..084d1b8 100644 --- a/tools/patman/settings.py +++ b/tools/patman/settings.py @@ -26,6 +26,140 @@ import re import command import gitutil
+"""Default settings per-project. + +These are used by _ProjectConfigParser. Settings names should match +the "dest" of the option parser from patman.py. +""" +_default_settings = { + "u-boot": {}, + "linux": { + "process_tags": "False", + } +} + +class _ProjectConfigParser(ConfigParser.SafeConfigParser): + """ConfigParser that handles projects. + + There are two main goals of this class: + - Load project-specific default settings. + - Merge general default settings/aliases with project-specific ones. + + # Sample config used for tests below... + >>> import StringIO + >>> sample_config = ''' + ... [alias] + ... me: Peter P. likesspiders@example.com + ... enemies: Evil evil@example.com + ... + ... [sm_alias] + ... enemies: Green G. ugly@example.com + ... + ... [sm2_alias] + ... enemies: Doc O. pus@example.com + ... + ... [settings] + ... am_hero: True + ... ''' + + # Check to make sure that bogus project gets general alias. + >>> config = _ProjectConfigParser("zzz") + >>> config.readfp(StringIO.StringIO(sample_config)) + >>> config.get("alias", "enemies") + 'Evil evil@example.com' + + # Check to make sure that alias gets overridden by project. + >>> config = _ProjectConfigParser("sm") + >>> config.readfp(StringIO.StringIO(sample_config)) + >>> config.get("alias", "enemies") + 'Green G. ugly@example.com' + + # Check to make sure that settings get merged with project. + >>> config = _ProjectConfigParser("linux") + >>> config.readfp(StringIO.StringIO(sample_config)) + >>> sorted(config.items("settings")) + [('am_hero', 'True'), ('process_tags', 'False')] + + # Check to make sure that settings works with unknown project. + >>> config = _ProjectConfigParser("unknown") + >>> config.readfp(StringIO.StringIO(sample_config)) + >>> sorted(config.items("settings")) + [('am_hero', 'True')] + """ + def __init__(self, project_name): + """Construct _ProjectConfigParser. + + In addition to standard SafeConfigParser initialization, this also loads + project defaults. + + Args: + project_name: The name of the project. + """ + self._project_name = project_name + ConfigParser.SafeConfigParser.__init__(self) + + # Update the project settings in the config based on + # the _default_settings global. + project_settings = "%s_settings" % project_name + if not self.has_section(project_settings): + self.add_section(project_settings) + project_defaults = _default_settings.get(project_name, {}) + for setting_name, setting_value in project_defaults.iteritems(): + self.set(project_settings, setting_name, setting_value) + + def get(self, section, option, *args, **kwargs): + """Extend SafeConfigParser to try project_section before section. + + Args: + See SafeConfigParser. + Returns: + See SafeConfigParser. + """ + try: + return ConfigParser.SafeConfigParser.get( + self, "%s_%s" % (self._project_name, section), option, + *args, **kwargs + ) + except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): + return ConfigParser.SafeConfigParser.get( + self, section, option, *args, **kwargs + ) + + def items(self, section, *args, **kwargs): + """Extend SafeConfigParser to add project_section to section. + + Args: + See SafeConfigParser. + Returns: + See SafeConfigParser. + """ + project_items = [] + has_project_section = False + top_items = [] + + # Get items from the project section + try: + project_items = ConfigParser.SafeConfigParser.items( + self, "%s_%s" % (self._project_name, section), *args, **kwargs + ) + has_project_section = True + except ConfigParser.NoSectionError: + pass + + # Get top-level items + try: + top_items = ConfigParser.SafeConfigParser.items( + self, section, *args, **kwargs + ) + except ConfigParser.NoSectionError: + # If neither section exists raise the error on... + if not has_project_section: + raise + + item_dict = dict(top_items) + item_dict.update(project_items) + return item_dict.items() + def ReadGitAliases(fname): """Read a git alias file. This is in the form used by git:
@@ -102,7 +236,7 @@ def _UpdateDefaults(parser, config): Args: parser: An instance of an OptionParser whose defaults will be updated. - config: An instance of SafeConfigParser that we will query + config: An instance of _ProjectConfigParser that we will query for settings. """ defaults = parser.get_default_values() @@ -117,14 +251,16 @@ def _UpdateDefaults(parser, config): else: print "WARNING: Unknown setting %s" % name
-def Setup(parser, config_fname=''): +def Setup(parser, project_name, config_fname=''): """Set up the settings module by reading config files.
Args: parser: The parser to update + project_name: Name of project that we're working on; we'll look + for sections named "project_section" as well. config_fname: Config filename to read ('' for default) """ - config = ConfigParser.SafeConfigParser() + config = _ProjectConfigParser(project_name) if config_fname == '': config_fname = '%s/.patman' % os.getenv('HOME')
@@ -141,3 +277,8 @@ def Setup(parser, config_fname=''):
# These are the aliases we understand, indexed by alias. Each member is a list. alias = {} + +if __name__ == "__main__": + import doctest + + doctest.testmod()

Hi Doug,
On Fri, Nov 30, 2012 at 4:29 PM, Doug Anderson dianders@chromium.org wrote:
There are cases that we want to support different settings (or maybe even different aliases) for different projects. Add support for this by:
- Adding detection for two big projects: U-Boot and Linux.
- Adding default settings for Linux (U-Boot is already good with the standard patman defaults).
- Extend the new "settings" feature in .patman to specify per-project settings.
Signed-off-by: Doug Anderson dianders@chromium.org
tools/patman/README | 13 ++++ tools/patman/patman.py | 9 +++- tools/patman/project.py | 43 +++++++++++++ tools/patman/settings.py | 147 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 tools/patman/project.py
diff --git a/tools/patman/README b/tools/patman/README index 6ca5b5b..d294f3d 100644 --- a/tools/patman/README +++ b/tools/patman/README @@ -114,6 +114,19 @@ verbose: True <<<
+If you want to adjust settings (or aliases) that affect just a single +project you can add a section that looks like [project_settings] or +[project_alias]. If you want to use tags for your linux work, you could +do:
+>>>
+[linux_settings] +process_tags: True
+<<<
How to run it
diff --git a/tools/patman/patman.py b/tools/patman/patman.py index b327c67..54a252e 100755 --- a/tools/patman/patman.py +++ b/tools/patman/patman.py @@ -34,6 +34,7 @@ import checkpatch import command import gitutil import patchstream +import project import settings import terminal import test @@ -59,6 +60,9 @@ parser.add_option('--cc-cmd', dest='cc_cmd', type='string', action='store', default=None, help='Output cc list for patch file (used by git)') parser.add_option('--no-tags', action='store_false', dest='process_tags', default=True, help="Don't process subject tags as aliaes") +parser.add_option('--project', default=project.DetectProject(),
help="Project name; affects default option values and "
"aliases [default: %default]")
Can you please add a short option also - perhaps -p?
[snip]
Regards, Simon

Simon,
Thanks!
On Mon, Dec 3, 2012 at 3:04 PM, Simon Glass sjg@chromium.org wrote:
Hi Doug,
On Fri, Nov 30, 2012 at 4:29 PM, Doug Anderson dianders@chromium.org wrote:
There are cases that we want to support different settings (or maybe even different aliases) for different projects. Add support for this by:
- Adding detection for two big projects: U-Boot and Linux.
- Adding default settings for Linux (U-Boot is already good with the standard patman defaults).
- Extend the new "settings" feature in .patman to specify per-project settings.
Signed-off-by: Doug Anderson dianders@chromium.org
tools/patman/README | 13 ++++ tools/patman/patman.py | 9 +++- tools/patman/project.py | 43 +++++++++++++ tools/patman/settings.py | 147 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 tools/patman/project.py
diff --git a/tools/patman/README b/tools/patman/README index 6ca5b5b..d294f3d 100644 --- a/tools/patman/README +++ b/tools/patman/README @@ -114,6 +114,19 @@ verbose: True <<<
+If you want to adjust settings (or aliases) that affect just a single +project you can add a section that looks like [project_settings] or +[project_alias]. If you want to use tags for your linux work, you could +do:
+>>>
+[linux_settings] +process_tags: True
+<<<
How to run it
diff --git a/tools/patman/patman.py b/tools/patman/patman.py index b327c67..54a252e 100755 --- a/tools/patman/patman.py +++ b/tools/patman/patman.py @@ -34,6 +34,7 @@ import checkpatch import command import gitutil import patchstream +import project import settings import terminal import test @@ -59,6 +60,9 @@ parser.add_option('--cc-cmd', dest='cc_cmd', type='string', action='store', default=None, help='Output cc list for patch file (used by git)') parser.add_option('--no-tags', action='store_false', dest='process_tags', default=True, help="Don't process subject tags as aliaes") +parser.add_option('--project', default=project.DetectProject(),
help="Project name; affects default option values and "
"aliases [default: %default]")
Can you please add a short option also - perhaps -p?
Done.
[snip]
Regards, Simon

The settings modules now has doctests, so run them.
Signed-off-by: Doug Anderson dianders@chromium.org --- tools/patman/patman.py | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/tools/patman/patman.py b/tools/patman/patman.py index 54a252e..6825de4 100755 --- a/tools/patman/patman.py +++ b/tools/patman/patman.py @@ -85,8 +85,9 @@ if options.test: result = unittest.TestResult() suite.run(result)
- suite = doctest.DocTestSuite('gitutil') - suite.run(result) + for module in ['gitutil', 'settings']: + suite = doctest.DocTestSuite(module) + suite.run(result)
# TODO: Surely we can just 'print' result? print result

On Fri, Nov 30, 2012 at 4:29 PM, Doug Anderson dianders@chromium.org wrote:
The settings modules now has doctests, so run them.
Signed-off-by: Doug Anderson dianders@chromium.org
Acked-by: Simon Glass sjg@chromium.org
tools/patman/patman.py | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/tools/patman/patman.py b/tools/patman/patman.py index 54a252e..6825de4 100755 --- a/tools/patman/patman.py +++ b/tools/patman/patman.py @@ -85,8 +85,9 @@ if options.test: result = unittest.TestResult() suite.run(result)
- suite = doctest.DocTestSuite('gitutil')
- suite.run(result)
for module in ['gitutil', 'settings']:
suite = doctest.DocTestSuite(module)
suite.run(result)
# TODO: Surely we can just 'print' result? print result
-- 1.7.7.3

On Fri, Nov 30, 2012 at 4:29 PM, Doug Anderson dianders@chromium.org wrote:
For Linux the best way to figure out where to send a patch is with the "get_maintainer.pl" script. Add support for calling it from patman. Support is added unconditionally for "scripts/get_maintainer.pl" in case it is helpful for any other projects.
Signed-off-by: Doug Anderson dianders@chromium.org
Acked-by: Simon Glass sjg@chromium.org

For Linux the best way to figure out where to send a patch is with the "get_maintainer.pl" script. Add support for calling it from patman. Support is added unconditionally for "scripts/get_maintainer.pl" in case it is helpful for any other projects.
Signed-off-by: Doug Anderson dianders@chromium.org --- Changes in v2: None
tools/patman/README | 11 ++++++- tools/patman/get_maintainer.py | 63 ++++++++++++++++++++++++++++++++++++++++ tools/patman/series.py | 2 + 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 tools/patman/get_maintainer.py
diff --git a/tools/patman/README b/tools/patman/README index 5b6eba0..16b51eb 100644 --- a/tools/patman/README +++ b/tools/patman/README @@ -43,6 +43,9 @@ Series-to: fred.blogs@napier.co.nz
in one of your commits, the series will be sent there.
+In Linux this will also call get_maintainer.pl on each of your +patches automatically. +
How to use this tool ==================== @@ -65,8 +68,12 @@ will get a consistent result each time. How to configure it ===================
-For most cases patman will locate and use the file 'doc/git-mailrc' in -your U-Boot directory. This contains most of the aliases you will need. +For most cases of using patman for U-Boot developement patman will +locate and use the file 'doc/git-mailrc' in your U-Boot directory. +This contains most of the aliases you will need. + +For Linux the 'scripts/get_maintainer.pl' handles figuring out where +to send patches pretty well.
During the first run patman creates a config file for you by taking the default user name and email address from the global .gitconfig file. diff --git a/tools/patman/get_maintainer.py b/tools/patman/get_maintainer.py new file mode 100644 index 0000000..cb11373 --- /dev/null +++ b/tools/patman/get_maintainer.py @@ -0,0 +1,63 @@ +# Copyright (c) 2012 The Chromium OS Authors. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +import command +import gitutil +import os + +def FindGetMaintainer(): + """Look for the get_maintainer.pl script. + + Returns: + If the script is found we'll return a path to it; else None. + """ + try_list = [ + os.path.join(gitutil.GetTopLevel(), 'scripts'), + ] + # Look in the list + for path in try_list: + fname = os.path.join(path, 'get_maintainer.pl') + if os.path.isfile(fname): + return fname + + return None + +def GetMaintainer(fname, verbose=False): + """Run get_maintainer.pl on a file if we find it. + + We look for get_maintainer.pl in the 'scripts' directory at the top of + git. If we find it we'll run it. If we don't find get_maintainer.pl + then we fail silently. + + Args: + fname: Path to the patch file to run get_maintainer.pl on. + + Returns: + A list of email addresses to CC to. + """ + get_maintainer = FindGetMaintainer() + if not get_maintainer: + if verbose: + print "WARNING: Couldn't find get_maintainer.pl" + return [] + + stdout = command.Output(get_maintainer, '--norolestats', fname) + return stdout.splitlines() diff --git a/tools/patman/series.py b/tools/patman/series.py index 083af0f..6c5c570 100644 --- a/tools/patman/series.py +++ b/tools/patman/series.py @@ -22,6 +22,7 @@ import itertools import os
+import get_maintainer import gitutil import terminal
@@ -225,6 +226,7 @@ class Series(dict): if process_tags: list += gitutil.BuildEmailList(commit.tags) list += gitutil.BuildEmailList(commit.cc_list) + list += get_maintainer.GetMaintainer(commit.patch) all_ccs += list print >>fd, commit.patch, ', '.join(list) self._generated_cc[commit.patch] = list

This patch adds support for a [settings] section in the .patman file. In this section you can add settings that will affect the default values for command-line options.
Support is added in a generic way such that any setting can be updated by just referring to the "dest" of the option that is passed to the option parser. At the moment options that would make sense to put in settings are "ignore_errors", "process_tags", and "verbose". You could override them like:
[settings] ignore_errors: True process_tags: False verbose: True
The settings functionality is also used in a future change which adds support for per-project settings.
Signed-off-by: Doug Anderson dianders@chromium.org --- Changes in v2: None
tools/patman/README | 16 ++++++++++++++++ tools/patman/gitutil.py | 2 -- tools/patman/patman.py | 3 +++ tools/patman/settings.py | 39 +++++++++++++++++++++++++++++++++++---- 4 files changed, 54 insertions(+), 6 deletions(-)
diff --git a/tools/patman/README b/tools/patman/README index 16b51eb..2743da9 100644 --- a/tools/patman/README +++ b/tools/patman/README @@ -98,6 +98,22 @@ The checkpatch.pl in the U-Boot tools/ subdirectory will be located and used. Failing that you can put it into your path or ~/bin/checkpatch.pl
+If you want to change the defaults for patman's command-line arguments, +you can add a [settings] section to your .patman file. This can be used +for any command line option by referring to the "dest" for the option in +patman.py. For reference, the useful ones (at the moment) shown below +(all with the non-default setting): + +>>> + +[settings] +ignore_errors: True +process_tags: False +verbose: True + +<<< + + How to run it =============
diff --git a/tools/patman/gitutil.py b/tools/patman/gitutil.py index 72d37a0..e7753cf 100644 --- a/tools/patman/gitutil.py +++ b/tools/patman/gitutil.py @@ -377,8 +377,6 @@ def GetDefaultUserEmail():
def Setup(): """Set up git utils, by reading the alias files.""" - settings.Setup('') - # Check for a git alias file also alias_fname = GetAliasFile() if alias_fname: diff --git a/tools/patman/patman.py b/tools/patman/patman.py index 4181d80..b327c67 100755 --- a/tools/patman/patman.py +++ b/tools/patman/patman.py @@ -34,6 +34,7 @@ import checkpatch import command import gitutil import patchstream +import settings import terminal import test
@@ -64,6 +65,8 @@ parser.usage = """patman [options] Create patches from commits in a branch, check them and email them as specified by tags you place in the commits. Use -n to """
+ +settings.Setup(parser, '') (options, args) = parser.parse_args()
# Run our meagre tests diff --git a/tools/patman/settings.py b/tools/patman/settings.py index 4dda17b..5208f7d 100644 --- a/tools/patman/settings.py +++ b/tools/patman/settings.py @@ -88,13 +88,43 @@ def CreatePatmanConfigFile(config_fname): print >>f, "[alias]\nme: %s <%s>" % (name, email) f.close();
-def Setup(config_fname=''): +def _UpdateDefaults(parser, config): + """Update the given OptionParser defaults based on config. + + We'll walk through all of the settings from the parser + For each setting we'll look for a default in the option parser. + If it's found we'll update the option parser default. + + The idea here is that the .patman file should be able to update + defaults but that command line flags should still have the final + say. + + Args: + parser: An instance of an OptionParser whose defaults will be + updated. + config: An instance of SafeConfigParser that we will query + for settings. + """ + defaults = parser.get_default_values() + for name, val in config.items('settings'): + if hasattr(defaults, name): + default_val = getattr(defaults, name) + if isinstance(default_val, bool): + val = config.getboolean('settings', name) + elif isinstance(default_val, int): + val = config.getint('settings', name) + parser.set_default(name, val) + else: + print "WARNING: Unknown setting %s" % name + +def Setup(parser, config_fname=''): """Set up the settings module by reading config files.
Args: + parser: The parser to update config_fname: Config filename to read ('' for default) """ - settings = ConfigParser.SafeConfigParser() + config = ConfigParser.SafeConfigParser() if config_fname == '': config_fname = '%s/.patman' % os.getenv('HOME')
@@ -102,11 +132,12 @@ def Setup(config_fname=''): print "No config file found ~/.patman\nCreating one...\n" CreatePatmanConfigFile(config_fname)
- settings.read(config_fname) + config.read(config_fname)
- for name, value in settings.items('alias'): + for name, value in config.items('alias'): alias[name] = value.split(',')
+ _UpdateDefaults(parser, config)
# These are the aliases we understand, indexed by alias. Each member is a list. alias = {}

On Tue, Dec 4, 2012 at 1:43 PM, Doug Anderson dianders@chromium.org wrote:
This patch adds support for a [settings] section in the .patman file. In this section you can add settings that will affect the default values for command-line options.
Support is added in a generic way such that any setting can be updated by just referring to the "dest" of the option that is passed to the option parser. At the moment options that would make sense to put in settings are "ignore_errors", "process_tags", and "verbose". You could override them like:
[settings] ignore_errors: True process_tags: False verbose: True
The settings functionality is also used in a future change which adds support for per-project settings.
Signed-off-by: Doug Anderson dianders@chromium.org
Changes in v2: None
Applied to u-boot-x86, thanks.

There are cases that we want to support different settings (or maybe even different aliases) for different projects. Add support for this by: * Adding detection for two big projects: U-Boot and Linux. * Adding default settings for Linux (U-Boot is already good with the standard patman defaults). * Extend the new "settings" feature in .patman to specify per-project settings.
Signed-off-by: Doug Anderson dianders@chromium.org --- Changes in v2: - Added requested short option: '-p'.
tools/patman/README | 13 ++++ tools/patman/patman.py | 9 +++- tools/patman/project.py | 43 +++++++++++++ tools/patman/settings.py | 147 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 tools/patman/project.py
diff --git a/tools/patman/README b/tools/patman/README index 2743da9..1832ebd 100644 --- a/tools/patman/README +++ b/tools/patman/README @@ -114,6 +114,19 @@ verbose: True <<<
+If you want to adjust settings (or aliases) that affect just a single +project you can add a section that looks like [project_settings] or +[project_alias]. If you want to use tags for your linux work, you could +do: + +>>> + +[linux_settings] +process_tags: True + +<<< + + How to run it =============
diff --git a/tools/patman/patman.py b/tools/patman/patman.py index b327c67..2e9e5dc 100755 --- a/tools/patman/patman.py +++ b/tools/patman/patman.py @@ -34,6 +34,7 @@ import checkpatch import command import gitutil import patchstream +import project import settings import terminal import test @@ -59,6 +60,9 @@ parser.add_option('--cc-cmd', dest='cc_cmd', type='string', action='store', default=None, help='Output cc list for patch file (used by git)') parser.add_option('--no-tags', action='store_false', dest='process_tags', default=True, help="Don't process subject tags as aliaes") +parser.add_option('-p', '--project', default=project.DetectProject(), + help="Project name; affects default option values and " + "aliases [default: %default]")
parser.usage = """patman [options]
@@ -66,7 +70,10 @@ Create patches from commits in a branch, check them and email them as specified by tags you place in the commits. Use -n to """
-settings.Setup(parser, '') +# Parse options twice: first to get the project and second to handle +# defaults properly (which depends on project). +(options, args) = parser.parse_args() +settings.Setup(parser, options.project, '') (options, args) = parser.parse_args()
# Run our meagre tests diff --git a/tools/patman/project.py b/tools/patman/project.py new file mode 100644 index 0000000..4f7b2b3 --- /dev/null +++ b/tools/patman/project.py @@ -0,0 +1,43 @@ +# Copyright (c) 2012 The Chromium OS Authors. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +import os.path + +import gitutil + +def DetectProject(): + """Autodetect the name of the current project. + + This looks for signature files/directories that are unlikely to exist except + in the given project. + + Returns: + The name of the project, like "linux" or "u-boot". Returns "unknown" + if we can't detect the project. + """ + top_level = gitutil.GetTopLevel() + + if os.path.exists(os.path.join(top_level, "include", "u-boot")): + return "u-boot" + elif os.path.exists(os.path.join(top_level, "kernel")): + return "linux" + + return "unknown" diff --git a/tools/patman/settings.py b/tools/patman/settings.py index 5208f7d..084d1b8 100644 --- a/tools/patman/settings.py +++ b/tools/patman/settings.py @@ -26,6 +26,140 @@ import re import command import gitutil
+"""Default settings per-project. + +These are used by _ProjectConfigParser. Settings names should match +the "dest" of the option parser from patman.py. +""" +_default_settings = { + "u-boot": {}, + "linux": { + "process_tags": "False", + } +} + +class _ProjectConfigParser(ConfigParser.SafeConfigParser): + """ConfigParser that handles projects. + + There are two main goals of this class: + - Load project-specific default settings. + - Merge general default settings/aliases with project-specific ones. + + # Sample config used for tests below... + >>> import StringIO + >>> sample_config = ''' + ... [alias] + ... me: Peter P. likesspiders@example.com + ... enemies: Evil evil@example.com + ... + ... [sm_alias] + ... enemies: Green G. ugly@example.com + ... + ... [sm2_alias] + ... enemies: Doc O. pus@example.com + ... + ... [settings] + ... am_hero: True + ... ''' + + # Check to make sure that bogus project gets general alias. + >>> config = _ProjectConfigParser("zzz") + >>> config.readfp(StringIO.StringIO(sample_config)) + >>> config.get("alias", "enemies") + 'Evil evil@example.com' + + # Check to make sure that alias gets overridden by project. + >>> config = _ProjectConfigParser("sm") + >>> config.readfp(StringIO.StringIO(sample_config)) + >>> config.get("alias", "enemies") + 'Green G. ugly@example.com' + + # Check to make sure that settings get merged with project. + >>> config = _ProjectConfigParser("linux") + >>> config.readfp(StringIO.StringIO(sample_config)) + >>> sorted(config.items("settings")) + [('am_hero', 'True'), ('process_tags', 'False')] + + # Check to make sure that settings works with unknown project. + >>> config = _ProjectConfigParser("unknown") + >>> config.readfp(StringIO.StringIO(sample_config)) + >>> sorted(config.items("settings")) + [('am_hero', 'True')] + """ + def __init__(self, project_name): + """Construct _ProjectConfigParser. + + In addition to standard SafeConfigParser initialization, this also loads + project defaults. + + Args: + project_name: The name of the project. + """ + self._project_name = project_name + ConfigParser.SafeConfigParser.__init__(self) + + # Update the project settings in the config based on + # the _default_settings global. + project_settings = "%s_settings" % project_name + if not self.has_section(project_settings): + self.add_section(project_settings) + project_defaults = _default_settings.get(project_name, {}) + for setting_name, setting_value in project_defaults.iteritems(): + self.set(project_settings, setting_name, setting_value) + + def get(self, section, option, *args, **kwargs): + """Extend SafeConfigParser to try project_section before section. + + Args: + See SafeConfigParser. + Returns: + See SafeConfigParser. + """ + try: + return ConfigParser.SafeConfigParser.get( + self, "%s_%s" % (self._project_name, section), option, + *args, **kwargs + ) + except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): + return ConfigParser.SafeConfigParser.get( + self, section, option, *args, **kwargs + ) + + def items(self, section, *args, **kwargs): + """Extend SafeConfigParser to add project_section to section. + + Args: + See SafeConfigParser. + Returns: + See SafeConfigParser. + """ + project_items = [] + has_project_section = False + top_items = [] + + # Get items from the project section + try: + project_items = ConfigParser.SafeConfigParser.items( + self, "%s_%s" % (self._project_name, section), *args, **kwargs + ) + has_project_section = True + except ConfigParser.NoSectionError: + pass + + # Get top-level items + try: + top_items = ConfigParser.SafeConfigParser.items( + self, section, *args, **kwargs + ) + except ConfigParser.NoSectionError: + # If neither section exists raise the error on... + if not has_project_section: + raise + + item_dict = dict(top_items) + item_dict.update(project_items) + return item_dict.items() + def ReadGitAliases(fname): """Read a git alias file. This is in the form used by git:
@@ -102,7 +236,7 @@ def _UpdateDefaults(parser, config): Args: parser: An instance of an OptionParser whose defaults will be updated. - config: An instance of SafeConfigParser that we will query + config: An instance of _ProjectConfigParser that we will query for settings. """ defaults = parser.get_default_values() @@ -117,14 +251,16 @@ def _UpdateDefaults(parser, config): else: print "WARNING: Unknown setting %s" % name
-def Setup(parser, config_fname=''): +def Setup(parser, project_name, config_fname=''): """Set up the settings module by reading config files.
Args: parser: The parser to update + project_name: Name of project that we're working on; we'll look + for sections named "project_section" as well. config_fname: Config filename to read ('' for default) """ - config = ConfigParser.SafeConfigParser() + config = _ProjectConfigParser(project_name) if config_fname == '': config_fname = '%s/.patman' % os.getenv('HOME')
@@ -141,3 +277,8 @@ def Setup(parser, config_fname=''):
# These are the aliases we understand, indexed by alias. Each member is a list. alias = {} + +if __name__ == "__main__": + import doctest + + doctest.testmod()

On Mon, Dec 3, 2012 at 4:43 PM, Doug Anderson dianders@chromium.org wrote:
There are cases that we want to support different settings (or maybe even different aliases) for different projects. Add support for this by:
- Adding detection for two big projects: U-Boot and Linux.
- Adding default settings for Linux (U-Boot is already good with the standard patman defaults).
- Extend the new "settings" feature in .patman to specify per-project settings.
Signed-off-by: Doug Anderson dianders@chromium.org
Changes in v2:
- Added requested short option: '-p'.
Acked-by: Simon Glass sjg@chromium.org
tools/patman/README | 13 ++++ tools/patman/patman.py | 9 +++- tools/patman/project.py | 43 +++++++++++++ tools/patman/settings.py | 147 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 tools/patman/project.py

On Tue, Dec 4, 2012 at 1:55 PM, Simon Glass sjg@chromium.org wrote:
On Mon, Dec 3, 2012 at 4:43 PM, Doug Anderson dianders@chromium.org wrote:
There are cases that we want to support different settings (or maybe even different aliases) for different projects. Add support for this by:
- Adding detection for two big projects: U-Boot and Linux.
- Adding default settings for Linux (U-Boot is already good with the standard patman defaults).
- Extend the new "settings" feature in .patman to specify per-project settings.
Signed-off-by: Doug Anderson dianders@chromium.org
Changes in v2:
- Added requested short option: '-p'.
Acked-by: Simon Glass sjg@chromium.org
Applied to u-boot-x86, thanks.
tools/patman/README | 13 ++++ tools/patman/patman.py | 9 +++- tools/patman/project.py | 43 +++++++++++++ tools/patman/settings.py | 147 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 tools/patman/project.py

The settings modules now has doctests, so run them.
Signed-off-by: Doug Anderson dianders@chromium.org --- Changes in v2: None
tools/patman/patman.py | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/tools/patman/patman.py b/tools/patman/patman.py index 2e9e5dc..e56dd01 100755 --- a/tools/patman/patman.py +++ b/tools/patman/patman.py @@ -85,8 +85,9 @@ if options.test: result = unittest.TestResult() suite.run(result)
- suite = doctest.DocTestSuite('gitutil') - suite.run(result) + for module in ['gitutil', 'settings']: + suite = doctest.DocTestSuite(module) + suite.run(result)
# TODO: Surely we can just 'print' result? print result

On Tue, Dec 4, 2012 at 1:43 PM, Doug Anderson dianders@chromium.org wrote:
The settings modules now has doctests, so run them.
Signed-off-by: Doug Anderson dianders@chromium.org
Changes in v2: None
Applied to u-boot-x86, thanks.

On Tue, Dec 4, 2012 at 1:43 PM, Doug Anderson dianders@chromium.org wrote:
For Linux the best way to figure out where to send a patch is with the "get_maintainer.pl" script. Add support for calling it from patman. Support is added unconditionally for "scripts/get_maintainer.pl" in case it is helpful for any other projects.
Signed-off-by: Doug Anderson dianders@chromium.org
Changes in v2: None
Applied to u-boot-x86, thanks.
participants (2)
-
Doug Anderson
-
Simon Glass