194b13bbaSMasahiro Yamada#!/usr/bin/env python2 283d290c5STom Rini# SPDX-License-Identifier: GPL-2.0+ 30d24de9dSSimon Glass# 40d24de9dSSimon Glass# Copyright (c) 2011 The Chromium OS Authors. 50d24de9dSSimon Glass# 60d24de9dSSimon Glass 70d24de9dSSimon Glass"""See README for more information""" 80d24de9dSSimon Glass 90d24de9dSSimon Glassfrom optparse import OptionParser 100d24de9dSSimon Glassimport os 110d24de9dSSimon Glassimport re 120d24de9dSSimon Glassimport sys 130d24de9dSSimon Glassimport unittest 140d24de9dSSimon Glass 150d24de9dSSimon Glass# Our modules 16488d19cbSChris Packhamtry: 17488d19cbSChris Packham from patman import checkpatch, command, gitutil, patchstream, \ 18488d19cbSChris Packham project, settings, terminal, test 19488d19cbSChris Packhamexcept ImportError: 200d24de9dSSimon Glass import checkpatch 210d24de9dSSimon Glass import command 220d24de9dSSimon Glass import gitutil 230d24de9dSSimon Glass import patchstream 24a1dcee84SDoug Anderson import project 258568baedSDoug Anderson import settings 260d24de9dSSimon Glass import terminal 270d24de9dSSimon Glass import test 280d24de9dSSimon Glass 290d24de9dSSimon Glass 300d24de9dSSimon Glassparser = OptionParser() 310d24de9dSSimon Glassparser.add_option('-H', '--full-help', action='store_true', dest='full_help', 320d24de9dSSimon Glass default=False, help='Display the README file') 330d24de9dSSimon Glassparser.add_option('-c', '--count', dest='count', type='int', 340d24de9dSSimon Glass default=-1, help='Automatically create patches from top n commits') 350d24de9dSSimon Glassparser.add_option('-i', '--ignore-errors', action='store_true', 360d24de9dSSimon Glass dest='ignore_errors', default=False, 370d24de9dSSimon Glass help='Send patches email even if patch errors are found') 38983a2749SSimon Glassparser.add_option('-m', '--no-maintainers', action='store_false', 39983a2749SSimon Glass dest='add_maintainers', default=True, 40983a2749SSimon Glass help="Don't cc the file maintainers automatically") 414fb35029SChris Packhamparser.add_option('-l', '--limit-cc', dest='limit', type='int', 424fb35029SChris Packham default=None, help='Limit the cc list to LIMIT entries [default: %default]') 430d24de9dSSimon Glassparser.add_option('-n', '--dry-run', action='store_true', dest='dry_run', 44ca706e76SSimon Glass default=False, help="Do a dry run (create but don't email patches)") 4599adf6edSVadim Bendeburyparser.add_option('-p', '--project', default=project.DetectProject(), 4699adf6edSVadim Bendebury help="Project name; affects default option values and " 4799adf6edSVadim Bendebury "aliases [default: %default]") 486d819925SDoug Andersonparser.add_option('-r', '--in-reply-to', type='string', action='store', 496d819925SDoug Anderson help="Message ID that this series is in reply to") 500d24de9dSSimon Glassparser.add_option('-s', '--start', dest='start', type='int', 510d24de9dSSimon Glass default=0, help='Commit to start creating patches from (0 = HEAD)') 52a1318f7cSSimon Glassparser.add_option('-t', '--ignore-bad-tags', action='store_true', 53a1318f7cSSimon Glass default=False, help='Ignore bad tags / aliases') 54a1318f7cSSimon Glassparser.add_option('--test', action='store_true', dest='test', 550d24de9dSSimon Glass default=False, help='run tests') 560d24de9dSSimon Glassparser.add_option('-v', '--verbose', action='store_true', dest='verbose', 570d24de9dSSimon Glass default=False, help='Verbose output of errors and warnings') 580d24de9dSSimon Glassparser.add_option('--cc-cmd', dest='cc_cmd', type='string', action='store', 590d24de9dSSimon Glass default=None, help='Output cc list for patch file (used by git)') 6099adf6edSVadim Bendeburyparser.add_option('--no-check', action='store_false', dest='check_patch', 6199adf6edSVadim Bendebury default=True, 6299adf6edSVadim Bendebury help="Don't check for patch compliance") 630d24de9dSSimon Glassparser.add_option('--no-tags', action='store_false', dest='process_tags', 640d24de9dSSimon Glass default=True, help="Don't process subject tags as aliaes") 65*a60aedfdSSimon Glassparser.add_option('--smtp-server', type='str', 66*a60aedfdSSimon Glass help="Specify the SMTP server to 'git send-email'") 6727067a46SMateusz Kulikowskiparser.add_option('-T', '--thread', action='store_true', dest='thread', 6827067a46SMateusz Kulikowski default=False, help='Create patches as a single thread') 690d24de9dSSimon Glass 70e0a4d06aSMasahiro Yamadaparser.usage += """ 710d24de9dSSimon Glass 720d24de9dSSimon GlassCreate patches from commits in a branch, check them and email them as 73ca706e76SSimon Glassspecified by tags you place in the commits. Use -n to do a dry run first.""" 740d24de9dSSimon Glass 758568baedSDoug Anderson 76a1dcee84SDoug Anderson# Parse options twice: first to get the project and second to handle 77a1dcee84SDoug Anderson# defaults properly (which depends on project). 78a1dcee84SDoug Anderson(options, args) = parser.parse_args() 79a1dcee84SDoug Andersonsettings.Setup(parser, options.project, '') 800d24de9dSSimon Glass(options, args) = parser.parse_args() 810d24de9dSSimon Glass 829649e152SSimon Glassif __name__ != "__main__": 839649e152SSimon Glass pass 849649e152SSimon Glass 850d24de9dSSimon Glass# Run our meagre tests 869649e152SSimon Glasselif options.test: 870d24de9dSSimon Glass import doctest 886e87ae1cSSimon Glass import func_test 890d24de9dSSimon Glass 900d24de9dSSimon Glass sys.argv = [sys.argv[0]] 910d24de9dSSimon Glass result = unittest.TestResult() 926e87ae1cSSimon Glass for module in (test.TestPatch, func_test.TestFunctional): 936e87ae1cSSimon Glass suite = unittest.TestLoader().loadTestsFromTestCase(module) 940d24de9dSSimon Glass suite.run(result) 950d24de9dSSimon Glass 96656cffebSDoug Anderson for module in ['gitutil', 'settings']: 97656cffebSDoug Anderson suite = doctest.DocTestSuite(module) 980d24de9dSSimon Glass suite.run(result) 990d24de9dSSimon Glass 1000d24de9dSSimon Glass # TODO: Surely we can just 'print' result? 101a920a17bSPaul Burton print(result) 1020d24de9dSSimon Glass for test, err in result.errors: 103a920a17bSPaul Burton print(err) 1040d24de9dSSimon Glass for test, err in result.failures: 105a920a17bSPaul Burton print(err) 1060d24de9dSSimon Glass 1070d24de9dSSimon Glass# Called from git with a patch filename as argument 1080d24de9dSSimon Glass# Printout a list of additional CC recipients for this patch 1090d24de9dSSimon Glasselif options.cc_cmd: 1100d24de9dSSimon Glass fd = open(options.cc_cmd, 'r') 1110d24de9dSSimon Glass re_line = re.compile('(\S*) (.*)') 1120d24de9dSSimon Glass for line in fd.readlines(): 1130d24de9dSSimon Glass match = re_line.match(line) 1140d24de9dSSimon Glass if match and match.group(1) == args[0]: 1150d24de9dSSimon Glass for cc in match.group(2).split(', '): 1160d24de9dSSimon Glass cc = cc.strip() 1170d24de9dSSimon Glass if cc: 118a920a17bSPaul Burton print(cc) 1190d24de9dSSimon Glass fd.close() 1200d24de9dSSimon Glass 1210d24de9dSSimon Glasselif options.full_help: 1220d24de9dSSimon Glass pager = os.getenv('PAGER') 1230d24de9dSSimon Glass if not pager: 1240d24de9dSSimon Glass pager = 'more' 1252bdeade0SSimon Glass fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), 1262bdeade0SSimon Glass 'README') 1270d24de9dSSimon Glass command.Run(pager, fname) 1280d24de9dSSimon Glass 1290d24de9dSSimon Glass# Process commits, produce patches files, check them, email them 1300d24de9dSSimon Glasselse: 1310d24de9dSSimon Glass gitutil.Setup() 1320d24de9dSSimon Glass 1330d24de9dSSimon Glass if options.count == -1: 1340d24de9dSSimon Glass # Work out how many patches to send if we can 1350d24de9dSSimon Glass options.count = gitutil.CountCommitsToBranch() - options.start 1360d24de9dSSimon Glass 1370d24de9dSSimon Glass col = terminal.Color() 1380d24de9dSSimon Glass if not options.count: 1390d24de9dSSimon Glass str = 'No commits found to process - please use -c flag' 14031e2141dSMasahiro Yamada sys.exit(col.Color(col.RED, str)) 1410d24de9dSSimon Glass 1420d24de9dSSimon Glass # Read the metadata from the commits 1430d24de9dSSimon Glass if options.count: 1440d24de9dSSimon Glass series = patchstream.GetMetaData(options.start, options.count) 1450d24de9dSSimon Glass cover_fname, args = gitutil.CreatePatches(options.start, options.count, 1460d24de9dSSimon Glass series) 1470d24de9dSSimon Glass 1480d24de9dSSimon Glass # Fix up the patch files to our liking, and insert the cover letter 149db116cc8SSimon Glass patchstream.FixPatches(series, args) 150db116cc8SSimon Glass if cover_fname and series.get('cover'): 1510d24de9dSSimon Glass patchstream.InsertCoverLetter(cover_fname, series, options.count) 1520d24de9dSSimon Glass 1530d24de9dSSimon Glass # Do a few checks on the series 1540d24de9dSSimon Glass series.DoChecks() 1550d24de9dSSimon Glass 1560d24de9dSSimon Glass # Check the patches, and run them through 'git am' just to be sure 15799adf6edSVadim Bendebury if options.check_patch: 1580d24de9dSSimon Glass ok = checkpatch.CheckPatches(options.verbose, args) 15999adf6edSVadim Bendebury else: 16099adf6edSVadim Bendebury ok = True 1610d24de9dSSimon Glass 162a1318f7cSSimon Glass cc_file = series.MakeCcFile(options.process_tags, cover_fname, 163983a2749SSimon Glass not options.ignore_bad_tags, 1644fb35029SChris Packham options.add_maintainers, options.limit) 165d94566a1SDoug Anderson 1660d24de9dSSimon Glass # Email the patches out (giving the user time to check / cancel) 1670d24de9dSSimon Glass cmd = '' 1681f727885SVadim Bendebury its_a_go = ok or options.ignore_errors 1691f727885SVadim Bendebury if its_a_go: 1700d24de9dSSimon Glass cmd = gitutil.EmailPatches(series, cover_fname, args, 171a1318f7cSSimon Glass options.dry_run, not options.ignore_bad_tags, cc_file, 172*a60aedfdSSimon Glass in_reply_to=options.in_reply_to, thread=options.thread, 173*a60aedfdSSimon Glass smtp_server=options.smtp_server) 1741f727885SVadim Bendebury else: 175a920a17bSPaul Burton print(col.Color(col.RED, "Not sending emails due to errors/warnings")) 1760d24de9dSSimon Glass 1770d24de9dSSimon Glass # For a dry run, just show our actions as a sanity check 1780d24de9dSSimon Glass if options.dry_run: 1790d24de9dSSimon Glass series.ShowActions(args, cmd, options.process_tags) 1801f727885SVadim Bendebury if not its_a_go: 181a920a17bSPaul Burton print(col.Color(col.RED, "Email would not be sent")) 182d94566a1SDoug Anderson 183d94566a1SDoug Anderson os.remove(cc_file) 184