1e8570757SMichal Simek#!/usr/bin/env python 20d24de9dSSimon Glass# 30d24de9dSSimon Glass# Copyright (c) 2011 The Chromium OS Authors. 40d24de9dSSimon Glass# 51a459660SWolfgang Denk# SPDX-License-Identifier: GPL-2.0+ 60d24de9dSSimon Glass# 70d24de9dSSimon Glass 80d24de9dSSimon Glass"""See README for more information""" 90d24de9dSSimon Glass 100d24de9dSSimon Glassfrom optparse import OptionParser 110d24de9dSSimon Glassimport os 120d24de9dSSimon Glassimport re 130d24de9dSSimon Glassimport sys 140d24de9dSSimon Glassimport unittest 150d24de9dSSimon Glass 160d24de9dSSimon Glass# Our modules 17488d19cbSChris Packhamtry: 18488d19cbSChris Packham from patman import checkpatch, command, gitutil, patchstream, \ 19488d19cbSChris Packham project, settings, terminal, test 20488d19cbSChris Packhamexcept ImportError: 210d24de9dSSimon Glass import checkpatch 220d24de9dSSimon Glass import command 230d24de9dSSimon Glass import gitutil 240d24de9dSSimon Glass import patchstream 25a1dcee84SDoug Anderson import project 268568baedSDoug Anderson import settings 270d24de9dSSimon Glass import terminal 280d24de9dSSimon Glass import test 290d24de9dSSimon Glass 300d24de9dSSimon Glass 310d24de9dSSimon Glassparser = OptionParser() 320d24de9dSSimon Glassparser.add_option('-H', '--full-help', action='store_true', dest='full_help', 330d24de9dSSimon Glass default=False, help='Display the README file') 340d24de9dSSimon Glassparser.add_option('-c', '--count', dest='count', type='int', 350d24de9dSSimon Glass default=-1, help='Automatically create patches from top n commits') 360d24de9dSSimon Glassparser.add_option('-i', '--ignore-errors', action='store_true', 370d24de9dSSimon Glass dest='ignore_errors', default=False, 380d24de9dSSimon Glass help='Send patches email even if patch errors are found') 39983a2749SSimon Glassparser.add_option('-m', '--no-maintainers', action='store_false', 40983a2749SSimon Glass dest='add_maintainers', default=True, 41983a2749SSimon Glass help="Don't cc the file maintainers automatically") 420d24de9dSSimon Glassparser.add_option('-n', '--dry-run', action='store_true', dest='dry_run', 43ca706e76SSimon Glass default=False, help="Do a dry run (create but don't email patches)") 4499adf6edSVadim Bendeburyparser.add_option('-p', '--project', default=project.DetectProject(), 4599adf6edSVadim Bendebury help="Project name; affects default option values and " 4699adf6edSVadim Bendebury "aliases [default: %default]") 476d819925SDoug Andersonparser.add_option('-r', '--in-reply-to', type='string', action='store', 486d819925SDoug Anderson help="Message ID that this series is in reply to") 490d24de9dSSimon Glassparser.add_option('-s', '--start', dest='start', type='int', 500d24de9dSSimon Glass default=0, help='Commit to start creating patches from (0 = HEAD)') 51a1318f7cSSimon Glassparser.add_option('-t', '--ignore-bad-tags', action='store_true', 52a1318f7cSSimon Glass default=False, help='Ignore bad tags / aliases') 53a1318f7cSSimon Glassparser.add_option('--test', action='store_true', dest='test', 540d24de9dSSimon Glass default=False, help='run tests') 550d24de9dSSimon Glassparser.add_option('-v', '--verbose', action='store_true', dest='verbose', 560d24de9dSSimon Glass default=False, help='Verbose output of errors and warnings') 570d24de9dSSimon Glassparser.add_option('--cc-cmd', dest='cc_cmd', type='string', action='store', 580d24de9dSSimon Glass default=None, help='Output cc list for patch file (used by git)') 5999adf6edSVadim Bendeburyparser.add_option('--no-check', action='store_false', dest='check_patch', 6099adf6edSVadim Bendebury default=True, 6199adf6edSVadim Bendebury help="Don't check for patch compliance") 620d24de9dSSimon Glassparser.add_option('--no-tags', action='store_false', dest='process_tags', 630d24de9dSSimon Glass default=True, help="Don't process subject tags as aliaes") 6427067a46SMateusz Kulikowskiparser.add_option('-T', '--thread', action='store_true', dest='thread', 6527067a46SMateusz Kulikowski default=False, help='Create patches as a single thread') 660d24de9dSSimon Glass 67e0a4d06aSMasahiro Yamadaparser.usage += """ 680d24de9dSSimon Glass 690d24de9dSSimon GlassCreate patches from commits in a branch, check them and email them as 70ca706e76SSimon Glassspecified by tags you place in the commits. Use -n to do a dry run first.""" 710d24de9dSSimon Glass 728568baedSDoug Anderson 73a1dcee84SDoug Anderson# Parse options twice: first to get the project and second to handle 74a1dcee84SDoug Anderson# defaults properly (which depends on project). 75a1dcee84SDoug Anderson(options, args) = parser.parse_args() 76a1dcee84SDoug Andersonsettings.Setup(parser, options.project, '') 770d24de9dSSimon Glass(options, args) = parser.parse_args() 780d24de9dSSimon Glass 799649e152SSimon Glassif __name__ != "__main__": 809649e152SSimon Glass pass 819649e152SSimon Glass 820d24de9dSSimon Glass# Run our meagre tests 839649e152SSimon Glasselif options.test: 840d24de9dSSimon Glass import doctest 85*6e87ae1cSSimon Glass import func_test 860d24de9dSSimon Glass 870d24de9dSSimon Glass sys.argv = [sys.argv[0]] 880d24de9dSSimon Glass result = unittest.TestResult() 89*6e87ae1cSSimon Glass for module in (test.TestPatch, func_test.TestFunctional): 90*6e87ae1cSSimon Glass suite = unittest.TestLoader().loadTestsFromTestCase(module) 910d24de9dSSimon Glass suite.run(result) 920d24de9dSSimon Glass 93656cffebSDoug Anderson for module in ['gitutil', 'settings']: 94656cffebSDoug Anderson suite = doctest.DocTestSuite(module) 950d24de9dSSimon Glass suite.run(result) 960d24de9dSSimon Glass 970d24de9dSSimon Glass # TODO: Surely we can just 'print' result? 98a920a17bSPaul Burton print(result) 990d24de9dSSimon Glass for test, err in result.errors: 100a920a17bSPaul Burton print(err) 1010d24de9dSSimon Glass for test, err in result.failures: 102a920a17bSPaul Burton print(err) 1030d24de9dSSimon Glass 1040d24de9dSSimon Glass# Called from git with a patch filename as argument 1050d24de9dSSimon Glass# Printout a list of additional CC recipients for this patch 1060d24de9dSSimon Glasselif options.cc_cmd: 1070d24de9dSSimon Glass fd = open(options.cc_cmd, 'r') 1080d24de9dSSimon Glass re_line = re.compile('(\S*) (.*)') 1090d24de9dSSimon Glass for line in fd.readlines(): 1100d24de9dSSimon Glass match = re_line.match(line) 1110d24de9dSSimon Glass if match and match.group(1) == args[0]: 1120d24de9dSSimon Glass for cc in match.group(2).split(', '): 1130d24de9dSSimon Glass cc = cc.strip() 1140d24de9dSSimon Glass if cc: 115a920a17bSPaul Burton print(cc) 1160d24de9dSSimon Glass fd.close() 1170d24de9dSSimon Glass 1180d24de9dSSimon Glasselif options.full_help: 1190d24de9dSSimon Glass pager = os.getenv('PAGER') 1200d24de9dSSimon Glass if not pager: 1210d24de9dSSimon Glass pager = 'more' 1222bdeade0SSimon Glass fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), 1232bdeade0SSimon Glass 'README') 1240d24de9dSSimon Glass command.Run(pager, fname) 1250d24de9dSSimon Glass 1260d24de9dSSimon Glass# Process commits, produce patches files, check them, email them 1270d24de9dSSimon Glasselse: 1280d24de9dSSimon Glass gitutil.Setup() 1290d24de9dSSimon Glass 1300d24de9dSSimon Glass if options.count == -1: 1310d24de9dSSimon Glass # Work out how many patches to send if we can 1320d24de9dSSimon Glass options.count = gitutil.CountCommitsToBranch() - options.start 1330d24de9dSSimon Glass 1340d24de9dSSimon Glass col = terminal.Color() 1350d24de9dSSimon Glass if not options.count: 1360d24de9dSSimon Glass str = 'No commits found to process - please use -c flag' 13731e2141dSMasahiro Yamada sys.exit(col.Color(col.RED, str)) 1380d24de9dSSimon Glass 1390d24de9dSSimon Glass # Read the metadata from the commits 1400d24de9dSSimon Glass if options.count: 1410d24de9dSSimon Glass series = patchstream.GetMetaData(options.start, options.count) 1420d24de9dSSimon Glass cover_fname, args = gitutil.CreatePatches(options.start, options.count, 1430d24de9dSSimon Glass series) 1440d24de9dSSimon Glass 1450d24de9dSSimon Glass # Fix up the patch files to our liking, and insert the cover letter 146db116cc8SSimon Glass patchstream.FixPatches(series, args) 147db116cc8SSimon Glass if cover_fname and series.get('cover'): 1480d24de9dSSimon Glass patchstream.InsertCoverLetter(cover_fname, series, options.count) 1490d24de9dSSimon Glass 1500d24de9dSSimon Glass # Do a few checks on the series 1510d24de9dSSimon Glass series.DoChecks() 1520d24de9dSSimon Glass 1530d24de9dSSimon Glass # Check the patches, and run them through 'git am' just to be sure 15499adf6edSVadim Bendebury if options.check_patch: 1550d24de9dSSimon Glass ok = checkpatch.CheckPatches(options.verbose, args) 15699adf6edSVadim Bendebury else: 15799adf6edSVadim Bendebury ok = True 1580d24de9dSSimon Glass 159a1318f7cSSimon Glass cc_file = series.MakeCcFile(options.process_tags, cover_fname, 160983a2749SSimon Glass not options.ignore_bad_tags, 161983a2749SSimon Glass options.add_maintainers) 162d94566a1SDoug Anderson 1630d24de9dSSimon Glass # Email the patches out (giving the user time to check / cancel) 1640d24de9dSSimon Glass cmd = '' 1651f727885SVadim Bendebury its_a_go = ok or options.ignore_errors 1661f727885SVadim Bendebury if its_a_go: 1670d24de9dSSimon Glass cmd = gitutil.EmailPatches(series, cover_fname, args, 168a1318f7cSSimon Glass options.dry_run, not options.ignore_bad_tags, cc_file, 16927067a46SMateusz Kulikowski in_reply_to=options.in_reply_to, thread=options.thread) 1701f727885SVadim Bendebury else: 171a920a17bSPaul Burton print(col.Color(col.RED, "Not sending emails due to errors/warnings")) 1720d24de9dSSimon Glass 1730d24de9dSSimon Glass # For a dry run, just show our actions as a sanity check 1740d24de9dSSimon Glass if options.dry_run: 1750d24de9dSSimon Glass series.ShowActions(args, cmd, options.process_tags) 1761f727885SVadim Bendebury if not its_a_go: 177a920a17bSPaul Burton print(col.Color(col.RED, "Email would not be sent")) 178d94566a1SDoug Anderson 179d94566a1SDoug Anderson os.remove(cc_file) 180