From 05d5282b587f6fb7e34e1014e7312d2c25bb9a7f Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Mon, 26 Nov 2012 15:21:39 +0000 Subject: [PATCH 01/10] patman: Add spaces back into patman test The patman test code was failing because some extra spaces got stripped when it was applied. These spaces are critical to the test code working. Signed-off-by: Doug Anderson Acked-by: Simon Glass --- tools/patman/test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/patman/test.py b/tools/patman/test.py index cf42480a6..f801cedc7 100644 --- a/tools/patman/test.py +++ b/tools/patman/test.py @@ -119,8 +119,8 @@ index 6f3748d..f9e4e65 100644 --- a/README +++ b/README @@ -2026,6 +2026,17 @@ The following options need to be configured: - example, some LED's) on your board. At the moment, - the following checkpoints are implemented: + example, some LED's) on your board. At the moment, + the following checkpoints are implemented: +- Time boot progress + CONFIG_BOOTSTAGE @@ -134,7 +134,7 @@ index 6f3748d..f9e4e65 100644 + You can add calls to bootstage_mark() to set time markers. + - Standalone program support: - CONFIG_STANDALONE_LOAD_ADDR + CONFIG_STANDALONE_LOAD_ADDR diff --git a/common/bootstage.c b/common/bootstage.c new file mode 100644 From a970048e755768e7d7d10c6b209999133ef75814 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Mon, 26 Nov 2012 15:21:40 +0000 Subject: [PATCH 02/10] patman: Allow tests to run even if patman is in the path Several of the patman doctests assume that patman was run with: ./patman Fix them so that they work even if patman is run with just "patman" (because patman is in the path). Signed-off-by: Doug Anderson Acked-by: Simon Glass --- tools/patman/gitutil.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tools/patman/gitutil.py b/tools/patman/gitutil.py index 72d37a0b0..41a74a55a 100644 --- a/tools/patman/gitutil.py +++ b/tools/patman/gitutil.py @@ -217,6 +217,10 @@ def EmailPatches(series, cover_fname, args, dry_run, cc_fname, Returns: Git command that was/would be run + # For the duration of this doctest pretend that we ran patman with ./patman + >>> _old_argv0 = sys.argv[0] + >>> sys.argv[0] = './patman' + >>> alias = {} >>> alias['fred'] = ['f.bloggs@napier.co.nz'] >>> alias['john'] = ['j.bloggs@napier.co.nz'] @@ -244,6 +248,9 @@ def EmailPatches(series, cover_fname, args, dry_run, cc_fname, 'git send-email --annotate --to "f.bloggs@napier.co.nz" --cc \ "f.bloggs@napier.co.nz" --cc "j.bloggs@napier.co.nz" --cc \ "m.poppins@cloud.net" --cc-cmd "./patman --cc-cmd cc-fname" cover p1 p2' + + # Restore argv[0] since we clobbered it. + >>> sys.argv[0] = _old_argv0 """ to = BuildEmailList(series.get('to'), '--to', alias) if not to: @@ -340,8 +347,8 @@ def GetTopLevel(): This test makes sure that we are running tests in the right subdir - >>> os.path.realpath(os.getcwd()) == \ - os.path.join(GetTopLevel(), 'tools', 'scripts', 'patman') + >>> os.path.realpath(os.path.dirname(__file__)) == \ + os.path.join(GetTopLevel(), 'tools', 'patman') True """ return command.OutputOneLine('git', 'rev-parse', '--show-toplevel') From d96ef37df78eac5c636866d8f226991a5d96e6bb Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Mon, 26 Nov 2012 15:23:23 +0000 Subject: [PATCH 03/10] patman: Look for checkpatch in the scripts directory The Linux kernel stores checkpatch.pl in the scripts directory. Add that to the search path to make things more automatic for kernel development. Signed-off-by: Doug Anderson Acked-by: Simon Glass --- tools/patman/checkpatch.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/patman/checkpatch.py b/tools/patman/checkpatch.py index d831087d8..f72f8ee26 100644 --- a/tools/patman/checkpatch.py +++ b/tools/patman/checkpatch.py @@ -26,10 +26,12 @@ import re import terminal def FindCheckPatch(): + top_level = gitutil.GetTopLevel() try_list = [ os.getcwd(), os.path.join(os.getcwd(), '..', '..'), - os.path.join(gitutil.GetTopLevel(), 'tools'), + os.path.join(top_level, 'tools'), + os.path.join(top_level, 'scripts'), '%s/bin' % os.getenv('HOME'), ] # Look in current dir From d94566a1115d35f87db793d2daaa79823528937c Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Mon, 3 Dec 2012 14:40:42 +0000 Subject: [PATCH 04/10] patman: Cache the CC list from MakeCcFile() for use in ShowActions() Currently we go through and generate the CC list for patches twice. This gets slow when (in a future CL) we add a call to get_maintainer.pl on Linux. Instead of doing things twice, just cache the CC list when it is first generated. Signed-off-by: Doug Anderson Acked-by: Simon Glass --- tools/patman/patman.py | 6 ++++-- tools/patman/series.py | 13 +++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/tools/patman/patman.py b/tools/patman/patman.py index cfe06d082..de8314a16 100755 --- a/tools/patman/patman.py +++ b/tools/patman/patman.py @@ -140,14 +140,16 @@ else: options.count + options.start): ok = False + cc_file = series.MakeCcFile(options.process_tags) + # Email the patches out (giving the user time to check / cancel) cmd = '' if ok or options.ignore_errors: - cc_file = series.MakeCcFile(options.process_tags) cmd = gitutil.EmailPatches(series, cover_fname, args, options.dry_run, cc_file) - os.remove(cc_file) # For a dry run, just show our actions as a sanity check if options.dry_run: series.ShowActions(args, cmd, options.process_tags) + + os.remove(cc_file) diff --git a/tools/patman/series.py b/tools/patman/series.py index d2971f489..ad8288d3c 100644 --- a/tools/patman/series.py +++ b/tools/patman/series.py @@ -46,6 +46,11 @@ class Series(dict): self.notes = [] self.changes = {} + # Written in MakeCcFile() + # key: name of patch file + # value: list of email addresses + self._generated_cc = {} + # These make us more like a dictionary def __setattr__(self, name, value): self[name] = value @@ -109,10 +114,7 @@ class Series(dict): for upto in range(len(args)): commit = self.commits[upto] print col.Color(col.GREEN, ' %s' % args[upto]) - cc_list = [] - if process_tags: - cc_list += gitutil.BuildEmailList(commit.tags) - cc_list += gitutil.BuildEmailList(commit.cc_list) + cc_list = list(self._generated_cc[commit.patch]) # Skip items in To list if 'to' in self: @@ -202,6 +204,8 @@ class Series(dict): def MakeCcFile(self, process_tags): """Make a cc file for us to use for per-commit Cc automation + Also stores in self._generated_cc to make ShowActions() faster. + Args: process_tags: Process tags as if they were aliases Return: @@ -216,6 +220,7 @@ class Series(dict): list += gitutil.BuildEmailList(commit.tags) list += gitutil.BuildEmailList(commit.cc_list) print >>fd, commit.patch, ', '.join(list) + self._generated_cc[commit.patch] = list fd.close() return fname From 311872551570a6439f62ab476c6fd4836a7a9aa7 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Mon, 3 Dec 2012 14:40:43 +0000 Subject: [PATCH 05/10] patman: Add all CC addresses to the cover letter If we're sending a cover letter make sure to CC everyone that we're CCing on each of the individual patches. Signed-off-by: Doug Anderson --- tools/patman/README | 3 +++ tools/patman/patman.py | 2 +- tools/patman/series.py | 12 +++++++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/tools/patman/README b/tools/patman/README index dc3957ce6..5b6eba048 100644 --- a/tools/patman/README +++ b/tools/patman/README @@ -226,6 +226,9 @@ Date: Mon Nov 7 23:18:44 2011 -0500 will create a patch which is copied to x86, arm, sandbox, mikef, ag and afleming. +If you have a cover letter it will get sent to the union of the CC lists of +all of the other patches. + Example Work Flow ================= diff --git a/tools/patman/patman.py b/tools/patman/patman.py index de8314a16..4181d8078 100755 --- a/tools/patman/patman.py +++ b/tools/patman/patman.py @@ -140,7 +140,7 @@ else: options.count + options.start): ok = False - cc_file = series.MakeCcFile(options.process_tags) + cc_file = series.MakeCcFile(options.process_tags, cover_fname) # Email the patches out (giving the user time to check / cancel) cmd = '' diff --git a/tools/patman/series.py b/tools/patman/series.py index ad8288d3c..083af0f63 100644 --- a/tools/patman/series.py +++ b/tools/patman/series.py @@ -19,6 +19,7 @@ # MA 02111-1307 USA # +import itertools import os import gitutil @@ -138,6 +139,9 @@ class Series(dict): print 'Prefix:\t ', self.get('prefix') if self.cover: print 'Cover: %d lines' % len(self.cover) + all_ccs = itertools.chain(*self._generated_cc.values()) + for email in set(all_ccs): + print ' Cc: ',email if cmd: print 'Git command: %s' % cmd @@ -201,27 +205,33 @@ class Series(dict): str = 'Change log exists, but no version is set' print col.Color(col.RED, str) - def MakeCcFile(self, process_tags): + def MakeCcFile(self, process_tags, cover_fname): """Make a cc file for us to use for per-commit Cc automation Also stores in self._generated_cc to make ShowActions() faster. Args: process_tags: Process tags as if they were aliases + cover_fname: If non-None the name of the cover letter. Return: Filename of temp file created """ # Look for commit tags (of the form 'xxx:' at the start of the subject) fname = '/tmp/patman.%d' % os.getpid() fd = open(fname, 'w') + all_ccs = [] for commit in self.commits: list = [] if process_tags: list += gitutil.BuildEmailList(commit.tags) list += gitutil.BuildEmailList(commit.cc_list) + all_ccs += list print >>fd, commit.patch, ', '.join(list) self._generated_cc[commit.patch] = list + if cover_fname: + print >>fd, cover_fname, ', '.join(set(all_ccs)) + fd.close() return fname From 21a19d70e2c2aa45cfe62c6adf8ceee9fcfbcacb Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Mon, 3 Dec 2012 14:43:16 +0000 Subject: [PATCH 06/10] 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 --- 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 5b6eba048..16b51eb59 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 000000000..cb11373a0 --- /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 083af0f63..6c5c5702e 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 From 8568baed3bd9b4c0b8d71d1f933cdac459b0eae1 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Mon, 3 Dec 2012 14:43:17 +0000 Subject: [PATCH 07/10] patman: Add support for settings in .patman 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 --- 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 16b51eb59..2743da9eb 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 41a74a55a..ca3ba4a03 100644 --- a/tools/patman/gitutil.py +++ b/tools/patman/gitutil.py @@ -384,8 +384,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 4181d8078..b327c675f 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 4dda17bf5..5208f7df6 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 = {} From a1dcee84c993232a6c5a1f3b4e54952b587cf1d1 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Mon, 3 Dec 2012 14:43:18 +0000 Subject: [PATCH 08/10] patman: Add the concept of multiple projects 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 Acked-by: Simon Glass --- 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 2743da9eb..1832ebd18 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 b327c675f..2e9e5dc37 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 000000000..4f7b2b3ef --- /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 5208f7df6..084d1b80e 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. + ... enemies: Evil + ... + ... [sm_alias] + ... enemies: Green G. + ... + ... [sm2_alias] + ... enemies: Doc O. + ... + ... [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 ' + + # 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. ' + + # 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() From 656cffeb49394f1cc935001f90337028e01472c5 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Mon, 3 Dec 2012 14:43:19 +0000 Subject: [PATCH 09/10] patman: Add settings to the list of modules to doctest The settings modules now has doctests, so run them. Signed-off-by: Doug Anderson --- tools/patman/patman.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/patman/patman.py b/tools/patman/patman.py index 2e9e5dc37..e56dd0130 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 From 99adf6eda7bed1beb3fa3e18951342f67b108db7 Mon Sep 17 00:00:00 2001 From: Vadim Bendebury Date: Wed, 9 Jan 2013 16:00:10 +0000 Subject: [PATCH 10/10] patman: Allow use outside of u-boot tree To make it usable in git trees not providing a patch checker implementation, add a command line option, allowing to suppress patch check. While we are at it, sort debug options alphabetically. Also, do not raise an exception if checkpatch.pl is not found - just print an error message suggesting to use the new option, and return nonzero status. . unit test passes: $ ./patman -t . successfully used patman in the autotest tree to generate a patch email (with --no-check option) . successfully used patman in the u-boot tree to generate a patch email . `patman --help' now shows command line options ordered alphabetically Signed-off-by: Vadim Bendebury Acked-by: Doug Anderson Acked-by: Simon Glass --- tools/patman/checkpatch.py | 10 +++++----- tools/patman/patman.py | 14 ++++++++++---- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/tools/patman/checkpatch.py b/tools/patman/checkpatch.py index f72f8ee26..d3a0477bb 100644 --- a/tools/patman/checkpatch.py +++ b/tools/patman/checkpatch.py @@ -23,6 +23,7 @@ import command import gitutil import os import re +import sys import terminal def FindCheckPatch(): @@ -47,8 +48,10 @@ def FindCheckPatch(): if os.path.isfile(fname): return fname path = os.path.dirname(path) - print 'Could not find checkpatch.pl' - return None + + print >> sys.stderr, ('Cannot find checkpatch.pl - please put it in your ' + + '~/bin directory or use --no-check') + sys.exit(1) def CheckPatch(fname, verbose=False): """Run checkpatch.pl on a file. @@ -67,9 +70,6 @@ def CheckPatch(fname, verbose=False): error_count, warning_count, lines = 0, 0, 0 problems = [] chk = FindCheckPatch() - if not chk: - raise OSError, ('Cannot find checkpatch.pl - please put it in your ' + - '~/bin directory') item = {} stdout = command.Output(chk, '--no-tree', fname) #pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE) diff --git a/tools/patman/patman.py b/tools/patman/patman.py index e56dd0130..e049081ea 100755 --- a/tools/patman/patman.py +++ b/tools/patman/patman.py @@ -50,6 +50,9 @@ parser.add_option('-i', '--ignore-errors', action='store_true', help='Send patches email even if patch errors are found') parser.add_option('-n', '--dry-run', action='store_true', dest='dry_run', default=False, help="Do a try run (create but don't email patches)") +parser.add_option('-p', '--project', default=project.DetectProject(), + help="Project name; affects default option values and " + "aliases [default: %default]") parser.add_option('-s', '--start', dest='start', type='int', default=0, help='Commit to start creating patches from (0 = HEAD)') parser.add_option('-t', '--test', action='store_true', dest='test', @@ -58,11 +61,11 @@ parser.add_option('-v', '--verbose', action='store_true', dest='verbose', default=False, help='Verbose output of errors and warnings') 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-check', action='store_false', dest='check_patch', + default=True, + help="Don't check for patch compliance") 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] @@ -146,7 +149,10 @@ else: series.DoChecks() # Check the patches, and run them through 'git am' just to be sure - ok = checkpatch.CheckPatches(options.verbose, args) + if options.check_patch: + ok = checkpatch.CheckPatches(options.verbose, args) + else: + ok = True if not gitutil.ApplyPatches(options.verbose, args, options.count + options.start): ok = False