xref: /openbmc/u-boot/tools/patman/patman.py (revision 4ac5df4b)
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