10d24de9dSSimon Glass#!/usr/bin/python 20d24de9dSSimon Glass# 30d24de9dSSimon Glass# Copyright (c) 2011 The Chromium OS Authors. 40d24de9dSSimon Glass# 50d24de9dSSimon Glass# See file CREDITS for list of people who contributed to this 60d24de9dSSimon Glass# project. 70d24de9dSSimon Glass# 80d24de9dSSimon Glass# This program is free software; you can redistribute it and/or 90d24de9dSSimon Glass# modify it under the terms of the GNU General Public License as 100d24de9dSSimon Glass# published by the Free Software Foundation; either version 2 of 110d24de9dSSimon Glass# the License, or (at your option) any later version. 120d24de9dSSimon Glass# 130d24de9dSSimon Glass# This program is distributed in the hope that it will be useful, 140d24de9dSSimon Glass# but WITHOUT ANY WARRANTY; without even the implied warranty of 150d24de9dSSimon Glass# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 160d24de9dSSimon Glass# GNU General Public License for more details. 170d24de9dSSimon Glass# 180d24de9dSSimon Glass# You should have received a copy of the GNU General Public License 190d24de9dSSimon Glass# along with this program; if not, write to the Free Software 200d24de9dSSimon Glass# Foundation, Inc., 59 Temple Place, Suite 330, Boston, 210d24de9dSSimon Glass# MA 02111-1307 USA 220d24de9dSSimon Glass# 230d24de9dSSimon Glass 240d24de9dSSimon Glass"""See README for more information""" 250d24de9dSSimon Glass 260d24de9dSSimon Glassfrom optparse import OptionParser 270d24de9dSSimon Glassimport os 280d24de9dSSimon Glassimport re 290d24de9dSSimon Glassimport sys 300d24de9dSSimon Glassimport unittest 310d24de9dSSimon Glass 320d24de9dSSimon Glass# Our modules 330d24de9dSSimon Glassimport checkpatch 340d24de9dSSimon Glassimport command 350d24de9dSSimon Glassimport gitutil 360d24de9dSSimon Glassimport patchstream 37a1dcee84SDoug Andersonimport project 388568baedSDoug Andersonimport settings 390d24de9dSSimon Glassimport terminal 400d24de9dSSimon Glassimport test 410d24de9dSSimon Glass 420d24de9dSSimon Glass 430d24de9dSSimon Glassparser = OptionParser() 44*902a9715SSimon Glassparser.add_option('-a', '--no-apply', action='store_false', 45*902a9715SSimon Glass dest='apply_patches', default=True, 46*902a9715SSimon Glass help="Don't test-apply patches with git am") 470d24de9dSSimon Glassparser.add_option('-H', '--full-help', action='store_true', dest='full_help', 480d24de9dSSimon Glass default=False, help='Display the README file') 490d24de9dSSimon Glassparser.add_option('-c', '--count', dest='count', type='int', 500d24de9dSSimon Glass default=-1, help='Automatically create patches from top n commits') 510d24de9dSSimon Glassparser.add_option('-i', '--ignore-errors', action='store_true', 520d24de9dSSimon Glass dest='ignore_errors', default=False, 530d24de9dSSimon Glass help='Send patches email even if patch errors are found') 540d24de9dSSimon Glassparser.add_option('-n', '--dry-run', action='store_true', dest='dry_run', 55ca706e76SSimon Glass default=False, help="Do a dry run (create but don't email patches)") 5699adf6edSVadim Bendeburyparser.add_option('-p', '--project', default=project.DetectProject(), 5799adf6edSVadim Bendebury help="Project name; affects default option values and " 5899adf6edSVadim Bendebury "aliases [default: %default]") 596d819925SDoug Andersonparser.add_option('-r', '--in-reply-to', type='string', action='store', 606d819925SDoug Anderson help="Message ID that this series is in reply to") 610d24de9dSSimon Glassparser.add_option('-s', '--start', dest='start', type='int', 620d24de9dSSimon Glass default=0, help='Commit to start creating patches from (0 = HEAD)') 63a1318f7cSSimon Glassparser.add_option('-t', '--ignore-bad-tags', action='store_true', 64a1318f7cSSimon Glass default=False, help='Ignore bad tags / aliases') 65a1318f7cSSimon Glassparser.add_option('--test', action='store_true', dest='test', 660d24de9dSSimon Glass default=False, help='run tests') 670d24de9dSSimon Glassparser.add_option('-v', '--verbose', action='store_true', dest='verbose', 680d24de9dSSimon Glass default=False, help='Verbose output of errors and warnings') 690d24de9dSSimon Glassparser.add_option('--cc-cmd', dest='cc_cmd', type='string', action='store', 700d24de9dSSimon Glass default=None, help='Output cc list for patch file (used by git)') 7199adf6edSVadim Bendeburyparser.add_option('--no-check', action='store_false', dest='check_patch', 7299adf6edSVadim Bendebury default=True, 7399adf6edSVadim Bendebury help="Don't check for patch compliance") 740d24de9dSSimon Glassparser.add_option('--no-tags', action='store_false', dest='process_tags', 750d24de9dSSimon Glass default=True, help="Don't process subject tags as aliaes") 760d24de9dSSimon Glass 770d24de9dSSimon Glassparser.usage = """patman [options] 780d24de9dSSimon Glass 790d24de9dSSimon GlassCreate patches from commits in a branch, check them and email them as 80ca706e76SSimon Glassspecified by tags you place in the commits. Use -n to do a dry run first.""" 810d24de9dSSimon Glass 828568baedSDoug Anderson 83a1dcee84SDoug Anderson# Parse options twice: first to get the project and second to handle 84a1dcee84SDoug Anderson# defaults properly (which depends on project). 85a1dcee84SDoug Anderson(options, args) = parser.parse_args() 86a1dcee84SDoug Andersonsettings.Setup(parser, options.project, '') 870d24de9dSSimon Glass(options, args) = parser.parse_args() 880d24de9dSSimon Glass 890d24de9dSSimon Glass# Run our meagre tests 900d24de9dSSimon Glassif options.test: 910d24de9dSSimon Glass import doctest 920d24de9dSSimon Glass 930d24de9dSSimon Glass sys.argv = [sys.argv[0]] 940d24de9dSSimon Glass suite = unittest.TestLoader().loadTestsFromTestCase(test.TestPatch) 950d24de9dSSimon Glass result = unittest.TestResult() 960d24de9dSSimon Glass suite.run(result) 970d24de9dSSimon Glass 98656cffebSDoug Anderson for module in ['gitutil', 'settings']: 99656cffebSDoug Anderson suite = doctest.DocTestSuite(module) 1000d24de9dSSimon Glass suite.run(result) 1010d24de9dSSimon Glass 1020d24de9dSSimon Glass # TODO: Surely we can just 'print' result? 1030d24de9dSSimon Glass print result 1040d24de9dSSimon Glass for test, err in result.errors: 1050d24de9dSSimon Glass print err 1060d24de9dSSimon Glass for test, err in result.failures: 1070d24de9dSSimon Glass print err 1080d24de9dSSimon Glass 1090d24de9dSSimon Glass# Called from git with a patch filename as argument 1100d24de9dSSimon Glass# Printout a list of additional CC recipients for this patch 1110d24de9dSSimon Glasselif options.cc_cmd: 1120d24de9dSSimon Glass fd = open(options.cc_cmd, 'r') 1130d24de9dSSimon Glass re_line = re.compile('(\S*) (.*)') 1140d24de9dSSimon Glass for line in fd.readlines(): 1150d24de9dSSimon Glass match = re_line.match(line) 1160d24de9dSSimon Glass if match and match.group(1) == args[0]: 1170d24de9dSSimon Glass for cc in match.group(2).split(', '): 1180d24de9dSSimon Glass cc = cc.strip() 1190d24de9dSSimon Glass if cc: 1200d24de9dSSimon Glass print cc 1210d24de9dSSimon Glass fd.close() 1220d24de9dSSimon Glass 1230d24de9dSSimon Glasselif options.full_help: 1240d24de9dSSimon Glass pager = os.getenv('PAGER') 1250d24de9dSSimon Glass if not pager: 1260d24de9dSSimon Glass pager = 'more' 1270d24de9dSSimon Glass fname = os.path.join(os.path.dirname(sys.argv[0]), 'README') 1280d24de9dSSimon Glass command.Run(pager, fname) 1290d24de9dSSimon Glass 1300d24de9dSSimon Glass# Process commits, produce patches files, check them, email them 1310d24de9dSSimon Glasselse: 1320d24de9dSSimon Glass gitutil.Setup() 1330d24de9dSSimon Glass 1340d24de9dSSimon Glass if options.count == -1: 1350d24de9dSSimon Glass # Work out how many patches to send if we can 1360d24de9dSSimon Glass options.count = gitutil.CountCommitsToBranch() - options.start 1370d24de9dSSimon Glass 1380d24de9dSSimon Glass col = terminal.Color() 1390d24de9dSSimon Glass if not options.count: 1400d24de9dSSimon Glass str = 'No commits found to process - please use -c flag' 1410d24de9dSSimon Glass print col.Color(col.RED, str) 1420d24de9dSSimon Glass sys.exit(1) 1430d24de9dSSimon Glass 1440d24de9dSSimon Glass # Read the metadata from the commits 1450d24de9dSSimon Glass if options.count: 1460d24de9dSSimon Glass series = patchstream.GetMetaData(options.start, options.count) 1470d24de9dSSimon Glass cover_fname, args = gitutil.CreatePatches(options.start, options.count, 1480d24de9dSSimon Glass series) 1490d24de9dSSimon Glass 1500d24de9dSSimon Glass # Fix up the patch files to our liking, and insert the cover letter 1510d24de9dSSimon Glass series = patchstream.FixPatches(series, args) 1520d24de9dSSimon Glass if series and cover_fname and series.get('cover'): 1530d24de9dSSimon Glass patchstream.InsertCoverLetter(cover_fname, series, options.count) 1540d24de9dSSimon Glass 1550d24de9dSSimon Glass # Do a few checks on the series 1560d24de9dSSimon Glass series.DoChecks() 1570d24de9dSSimon Glass 1580d24de9dSSimon Glass # Check the patches, and run them through 'git am' just to be sure 15999adf6edSVadim Bendebury if options.check_patch: 1600d24de9dSSimon Glass ok = checkpatch.CheckPatches(options.verbose, args) 16199adf6edSVadim Bendebury else: 16299adf6edSVadim Bendebury ok = True 163*902a9715SSimon Glass if options.apply_patches: 1640d24de9dSSimon Glass if not gitutil.ApplyPatches(options.verbose, args, 1650d24de9dSSimon Glass options.count + options.start): 1660d24de9dSSimon Glass ok = False 1670d24de9dSSimon Glass 168a1318f7cSSimon Glass cc_file = series.MakeCcFile(options.process_tags, cover_fname, 169a1318f7cSSimon Glass not options.ignore_bad_tags) 170d94566a1SDoug Anderson 1710d24de9dSSimon Glass # Email the patches out (giving the user time to check / cancel) 1720d24de9dSSimon Glass cmd = '' 1730d24de9dSSimon Glass if ok or options.ignore_errors: 1740d24de9dSSimon Glass cmd = gitutil.EmailPatches(series, cover_fname, args, 175a1318f7cSSimon Glass options.dry_run, not options.ignore_bad_tags, cc_file, 176a1318f7cSSimon Glass in_reply_to=options.in_reply_to) 1770d24de9dSSimon Glass 1780d24de9dSSimon Glass # For a dry run, just show our actions as a sanity check 1790d24de9dSSimon Glass if options.dry_run: 1800d24de9dSSimon Glass series.ShowActions(args, cmd, options.process_tags) 181d94566a1SDoug Anderson 182d94566a1SDoug Anderson os.remove(cc_file) 183