xref: /illumos-gate/usr/src/tools/scripts/git-pbchk.py (revision 13904da86c95bce026575f75b430075604bb28e4)
1ca13eaa5SAndy Fiddaman#!@TOOLS_PYTHON@
28bcea973SRichard Lowe#
38bcea973SRichard Lowe#  This program is free software; you can redistribute it and/or modify
48bcea973SRichard Lowe#  it under the terms of the GNU General Public License version 2
58bcea973SRichard Lowe#  as published by the Free Software Foundation.
68bcea973SRichard Lowe#
78bcea973SRichard Lowe#  This program is distributed in the hope that it will be useful,
88bcea973SRichard Lowe#  but WITHOUT ANY WARRANTY; without even the implied warranty of
98bcea973SRichard Lowe#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
108bcea973SRichard Lowe#  GNU General Public License for more details.
118bcea973SRichard Lowe#
128bcea973SRichard Lowe#  You should have received a copy of the GNU General Public License
138bcea973SRichard Lowe#  along with this program; if not, write to the Free Software
148bcea973SRichard Lowe#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
158bcea973SRichard Lowe#
168bcea973SRichard Lowe
178bcea973SRichard Lowe#
188bcea973SRichard Lowe# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
198bcea973SRichard Lowe# Copyright 2008, 2012 Richard Lowe
20955eb5e1SGarrett D'Amore# Copyright 2019 Garrett D'Amore <garrett@damore.org>
2193d2a904SPaul Dagnelie# Copyright (c) 2015, 2016 by Delphix. All rights reserved.
2228e2b3adSHans Rosenfeld# Copyright 2016 Nexenta Systems, Inc.
23972282a0SJohn Levon# Copyright (c) 2019, Joyent, Inc.
24*13904da8SAndy Fiddaman# Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
258bcea973SRichard Lowe#
268bcea973SRichard Lowe
27ca13eaa5SAndy Fiddamanfrom __future__ import print_function
28ca13eaa5SAndy Fiddaman
298bcea973SRichard Loweimport getopt
30ca13eaa5SAndy Fiddamanimport io
318bcea973SRichard Loweimport os
328bcea973SRichard Loweimport re
338bcea973SRichard Loweimport subprocess
348bcea973SRichard Loweimport sys
35ff50e8e5SRichard Loweimport tempfile
368bcea973SRichard Lowe
37ca13eaa5SAndy Fiddamanif sys.version_info[0] < 3:
388bcea973SRichard Lowe    from cStringIO import StringIO
39ca13eaa5SAndy Fiddamanelse:
40ca13eaa5SAndy Fiddaman    from io import StringIO
418bcea973SRichard Lowe
428bcea973SRichard Lowe#
438bcea973SRichard Lowe# Adjust the load path based on our location and the version of python into
448bcea973SRichard Lowe# which it is being loaded.  This assumes the normal onbld directory
458bcea973SRichard Lowe# structure, where we are in bin/ and the modules are in
468bcea973SRichard Lowe# lib/python(version)?/onbld/Scm/.  If that changes so too must this.
478bcea973SRichard Lowe#
488bcea973SRichard Lowesys.path.insert(1, os.path.join(os.path.dirname(__file__), "..", "lib",
498bcea973SRichard Lowe                                "python%d.%d" % sys.version_info[:2]))
508bcea973SRichard Lowe
518bcea973SRichard Lowe#
528bcea973SRichard Lowe# Add the relative path to usr/src/tools to the load path, such that when run
538bcea973SRichard Lowe# from the source tree we use the modules also within the source tree.
548bcea973SRichard Lowe#
558bcea973SRichard Lowesys.path.insert(2, os.path.join(os.path.dirname(__file__), ".."))
568bcea973SRichard Lowe
57e5587435SJoshua M. Clulowfrom onbld.Scm import Ignore
584ff15898SGordon Rossfrom onbld.Checks import Comments, Copyright, CStyle, HdrChk, WsCheck
5971af3be3SCody Peter Mellofrom onbld.Checks import JStyle, Keywords, ManLint, Mapfile, SpellCheck
60*13904da8SAndy Fiddamanfrom onbld.Checks import ShellLint
618bcea973SRichard Lowe
628bcea973SRichard Loweclass GitError(Exception):
638bcea973SRichard Lowe    pass
648bcea973SRichard Lowe
658bcea973SRichard Lowedef git(command):
668bcea973SRichard Lowe    """Run a command and return a stream containing its stdout (and write its
678bcea973SRichard Lowe    stderr to its stdout)"""
688bcea973SRichard Lowe
698bcea973SRichard Lowe    if type(command) != list:
708bcea973SRichard Lowe        command = command.split()
718bcea973SRichard Lowe
728bcea973SRichard Lowe    command = ["git"] + command
738bcea973SRichard Lowe
74ff50e8e5SRichard Lowe    try:
75ca13eaa5SAndy Fiddaman        tmpfile = tempfile.TemporaryFile(prefix="git-nits", mode="w+b")
76ca13eaa5SAndy Fiddaman    except EnvironmentError as e:
77ff50e8e5SRichard Lowe        raise GitError("Could not create temporary file: %s\n" % e)
78ff50e8e5SRichard Lowe
79ff50e8e5SRichard Lowe    try:
808bcea973SRichard Lowe        p = subprocess.Popen(command,
81ff50e8e5SRichard Lowe                             stdout=tmpfile,
82380fd671SMatthew Ahrens                             stderr=subprocess.PIPE)
83ca13eaa5SAndy Fiddaman    except OSError as e:
84709afb1dSDillon Amburgey        raise GitError("could not execute %s: %s\n" % (command, e))
858bcea973SRichard Lowe
868bcea973SRichard Lowe    err = p.wait()
878bcea973SRichard Lowe    if err != 0:
88380fd671SMatthew Ahrens        raise GitError(p.stderr.read())
898bcea973SRichard Lowe
90ff50e8e5SRichard Lowe    tmpfile.seek(0)
91ca13eaa5SAndy Fiddaman    lines = []
92ca13eaa5SAndy Fiddaman    for l in tmpfile:
93ca13eaa5SAndy Fiddaman        lines.append(l.decode('utf-8', 'replace'))
94ca13eaa5SAndy Fiddaman    return lines
958bcea973SRichard Lowe
968bcea973SRichard Lowedef git_root():
978bcea973SRichard Lowe    """Return the root of the current git workspace"""
988bcea973SRichard Lowe
998bcea973SRichard Lowe    p = git('rev-parse --git-dir')
100ca13eaa5SAndy Fiddaman    dir = p[0]
1018bcea973SRichard Lowe
102ca13eaa5SAndy Fiddaman    return os.path.abspath(os.path.join(dir, os.path.pardir))
1038bcea973SRichard Lowe
1048bcea973SRichard Lowedef git_branch():
1058bcea973SRichard Lowe    """Return the current git branch"""
1068bcea973SRichard Lowe
1078bcea973SRichard Lowe    p = git('branch')
1088bcea973SRichard Lowe
1098bcea973SRichard Lowe    for elt in p:
1108bcea973SRichard Lowe        if elt[0] == '*':
1118bcea973SRichard Lowe            if elt.endswith('(no branch)'):
1128bcea973SRichard Lowe                return None
1138bcea973SRichard Lowe            return elt.split()[1]
1148bcea973SRichard Lowe
1158bcea973SRichard Lowedef git_parent_branch(branch):
1168bcea973SRichard Lowe    """Return the parent of the current git branch.
1178bcea973SRichard Lowe
1188bcea973SRichard Lowe    If this branch tracks a remote branch, return the remote branch which is
1198bcea973SRichard Lowe    tracked.  If not, default to origin/master."""
1208bcea973SRichard Lowe
1218bcea973SRichard Lowe    if not branch:
1228bcea973SRichard Lowe        return None
1238bcea973SRichard Lowe
12428e2b3adSHans Rosenfeld    p = git(["for-each-ref", "--format=%(refname:short) %(upstream:short)",
12528e2b3adSHans Rosenfeld            "refs/heads/"])
1268bcea973SRichard Lowe
1278bcea973SRichard Lowe    if not p:
1288bcea973SRichard Lowe        sys.stderr.write("Failed finding git parent branch\n")
129972282a0SJohn Levon        sys.exit(1)
1308bcea973SRichard Lowe
1318bcea973SRichard Lowe    for line in p:
1328bcea973SRichard Lowe        # Git 1.7 will leave a ' ' trailing any non-tracking branch
1338bcea973SRichard Lowe        if ' ' in line and not line.endswith(' \n'):
1348bcea973SRichard Lowe            local, remote = line.split()
1358bcea973SRichard Lowe            if local == branch:
1368bcea973SRichard Lowe                return remote
1378bcea973SRichard Lowe    return 'origin/master'
1388bcea973SRichard Lowe
1398bcea973SRichard Lowedef git_comments(parent):
1408bcea973SRichard Lowe    """Return a list of any checkin comments on this git branch"""
1418bcea973SRichard Lowe
14227495383SRichard Lowe    p = git('log --pretty=tformat:%%B:SEP: %s..' % parent)
1438bcea973SRichard Lowe
1448bcea973SRichard Lowe    if not p:
145972282a0SJohn Levon        sys.stderr.write("No outgoing changesets found - missing -p option?\n");
146972282a0SJohn Levon        sys.exit(1)
1478bcea973SRichard Lowe
148ca13eaa5SAndy Fiddaman    return [x.strip() for x in p if x != ':SEP:\n']
1498bcea973SRichard Lowe
1508bcea973SRichard Lowedef git_file_list(parent, paths=None):
1518bcea973SRichard Lowe    """Return the set of files which have ever changed on this branch.
1528bcea973SRichard Lowe
1538bcea973SRichard Lowe    NB: This includes files which no longer exist, or no longer actually
1548bcea973SRichard Lowe    differ."""
1558bcea973SRichard Lowe
1568bcea973SRichard Lowe    p = git("log --name-only --pretty=format: %s.. %s" %
1578bcea973SRichard Lowe             (parent, ' '.join(paths)))
1588bcea973SRichard Lowe
1598bcea973SRichard Lowe    if not p:
1608bcea973SRichard Lowe        sys.stderr.write("Failed building file-list from git\n")
161972282a0SJohn Levon        sys.exit(1)
1628bcea973SRichard Lowe
1638bcea973SRichard Lowe    ret = set()
1648bcea973SRichard Lowe    for fname in p:
16593d2a904SPaul Dagnelie        if fname and not fname.isspace() and fname not in ret:
1668bcea973SRichard Lowe            ret.add(fname.strip())
1678bcea973SRichard Lowe
1688bcea973SRichard Lowe    return ret
1698bcea973SRichard Lowe
1708bcea973SRichard Lowedef not_check(root, cmd):
1718bcea973SRichard Lowe    """Return a function which returns True if a file given as an argument
1728bcea973SRichard Lowe    should be excluded from the check named by 'cmd'"""
1738bcea973SRichard Lowe
174ca13eaa5SAndy Fiddaman    ignorefiles = list(filter(os.path.exists,
1758bcea973SRichard Lowe                         [os.path.join(root, ".git", "%s.NOT" % cmd),
176ca13eaa5SAndy Fiddaman                          os.path.join(root, "exception_lists", cmd)]))
177e5587435SJoshua M. Clulow    return Ignore.ignore(root, ignorefiles)
1788bcea973SRichard Lowe
179955eb5e1SGarrett D'Amoredef gen_files(root, parent, paths, exclude, filter=None):
1808bcea973SRichard Lowe    """Return a function producing file names, relative to the current
1818bcea973SRichard Lowe    directory, of any file changed on this branch (limited to 'paths' if
1828bcea973SRichard Lowe    requested), and excluding files for which exclude returns a true value """
1838bcea973SRichard Lowe
184955eb5e1SGarrett D'Amore    if filter is None:
185955eb5e1SGarrett D'Amore        filter = lambda x: os.path.isfile(x)
186955eb5e1SGarrett D'Amore
1878bcea973SRichard Lowe    # Taken entirely from Python 2.6's os.path.relpath which we would use if we
1888bcea973SRichard Lowe    # could.
1898bcea973SRichard Lowe    def relpath(path, here):
1908bcea973SRichard Lowe        c = os.path.abspath(os.path.join(root, path)).split(os.path.sep)
1918bcea973SRichard Lowe        s = os.path.abspath(here).split(os.path.sep)
1928bcea973SRichard Lowe        l = len(os.path.commonprefix((s, c)))
1938bcea973SRichard Lowe        return os.path.join(*[os.path.pardir] * (len(s)-l) + c[l:])
1948bcea973SRichard Lowe
1958bcea973SRichard Lowe    def ret(select=None):
1968bcea973SRichard Lowe        if not select:
1978bcea973SRichard Lowe            select = lambda x: True
1988bcea973SRichard Lowe
19938e36c53SJohn Levon        for abspath in git_file_list(parent, paths):
20038e36c53SJohn Levon            path = relpath(abspath, '.')
20193d2a904SPaul Dagnelie            try:
20238e36c53SJohn Levon                res = git("diff %s HEAD %s" % (parent, path))
203ca13eaa5SAndy Fiddaman            except GitError as e:
20438e36c53SJohn Levon                # This ignores all the errors that can be thrown. Usually, this
20538e36c53SJohn Levon                # means that git returned non-zero because the file doesn't
20638e36c53SJohn Levon                # exist, but it could also fail if git can't create a new file
20738e36c53SJohn Levon                # or it can't be executed.  Such errors are 1) unlikely, and 2)
20838e36c53SJohn Levon                # will be caught by other invocations of git().
20993d2a904SPaul Dagnelie                continue
210ca13eaa5SAndy Fiddaman            empty = not res
211955eb5e1SGarrett D'Amore            if (filter(path) and not empty and
21238e36c53SJohn Levon                select(path) and not exclude(abspath)):
21338e36c53SJohn Levon                yield path
2148bcea973SRichard Lowe    return ret
2158bcea973SRichard Lowe
216955eb5e1SGarrett D'Amoredef gen_links(root, parent, paths, exclude):
217955eb5e1SGarrett D'Amore    """Return a function producing symbolic link names, relative to the current
218955eb5e1SGarrett D'Amore    directory, of any file changed on this branch (limited to 'paths' if
219955eb5e1SGarrett D'Amore    requested), and excluding files for which exclude returns a true value """
220955eb5e1SGarrett D'Amore
221955eb5e1SGarrett D'Amore    return gen_files(root, parent, paths, exclude, lambda x: os.path.islink(x))
222955eb5e1SGarrett D'Amore
2238bcea973SRichard Lowedef comchk(root, parent, flist, output):
2248bcea973SRichard Lowe    output.write("Comments:\n")
2258bcea973SRichard Lowe
2268bcea973SRichard Lowe    return Comments.comchk(git_comments(parent), check_db=True,
2278bcea973SRichard Lowe                           output=output)
2288bcea973SRichard Lowe
2298bcea973SRichard Lowe
2308bcea973SRichard Lowedef mapfilechk(root, parent, flist, output):
2318bcea973SRichard Lowe    ret = 0
2328bcea973SRichard Lowe
2338bcea973SRichard Lowe    # We are interested in examining any file that has the following
2348bcea973SRichard Lowe    # in its final path segment:
2358bcea973SRichard Lowe    #    - Contains the word 'mapfile'
2368bcea973SRichard Lowe    #    - Begins with 'map.'
2378bcea973SRichard Lowe    #    - Ends with '.map'
2388bcea973SRichard Lowe    # We don't want to match unless these things occur in final path segment
2398bcea973SRichard Lowe    # because directory names with these strings don't indicate a mapfile.
2408bcea973SRichard Lowe    # We also ignore files with suffixes that tell us that the files
2418bcea973SRichard Lowe    # are not mapfiles.
2428bcea973SRichard Lowe    MapfileRE = re.compile(r'.*((mapfile[^/]*)|(/map\.+[^/]*)|(\.map))$',
2438bcea973SRichard Lowe        re.IGNORECASE)
2448bcea973SRichard Lowe    NotMapSuffixRE = re.compile(r'.*\.[ch]$', re.IGNORECASE)
2458bcea973SRichard Lowe
2468bcea973SRichard Lowe    output.write("Mapfile comments:\n")
2478bcea973SRichard Lowe
2488bcea973SRichard Lowe    for f in flist(lambda x: MapfileRE.match(x) and not
2498bcea973SRichard Lowe                   NotMapSuffixRE.match(x)):
250ca13eaa5SAndy Fiddaman        with io.open(f, encoding='utf-8', errors='replace') as fh:
2518bcea973SRichard Lowe            ret |= Mapfile.mapfilechk(fh, output=output)
2528bcea973SRichard Lowe    return ret
2538bcea973SRichard Lowe
2548bcea973SRichard Lowedef copyright(root, parent, flist, output):
2558bcea973SRichard Lowe    ret = 0
2568bcea973SRichard Lowe    output.write("Copyrights:\n")
2578bcea973SRichard Lowe    for f in flist():
258ca13eaa5SAndy Fiddaman        with io.open(f, encoding='utf-8', errors='replace') as fh:
2598bcea973SRichard Lowe            ret |= Copyright.copyright(fh, output=output)
2608bcea973SRichard Lowe    return ret
2618bcea973SRichard Lowe
2628bcea973SRichard Lowedef hdrchk(root, parent, flist, output):
2638bcea973SRichard Lowe    ret = 0
2648bcea973SRichard Lowe    output.write("Header format:\n")
2658bcea973SRichard Lowe    for f in flist(lambda x: x.endswith('.h')):
266ca13eaa5SAndy Fiddaman        with io.open(f, encoding='utf-8', errors='replace') as fh:
2678bcea973SRichard Lowe            ret |= HdrChk.hdrchk(fh, lenient=True, output=output)
2688bcea973SRichard Lowe    return ret
2698bcea973SRichard Lowe
2708bcea973SRichard Lowedef cstyle(root, parent, flist, output):
2718bcea973SRichard Lowe    ret = 0
2728bcea973SRichard Lowe    output.write("C style:\n")
2738bcea973SRichard Lowe    for f in flist(lambda x: x.endswith('.c') or x.endswith('.h')):
274a90997d2SAndy Fiddaman        with io.open(f, mode='rb') as fh:
2758bcea973SRichard Lowe            ret |= CStyle.cstyle(fh, output=output, picky=True,
2768bcea973SRichard Lowe                             check_posix_types=True,
2778bcea973SRichard Lowe                             check_continuation=True)
2788bcea973SRichard Lowe    return ret
2798bcea973SRichard Lowe
2808bcea973SRichard Lowedef jstyle(root, parent, flist, output):
2818bcea973SRichard Lowe    ret = 0
2828bcea973SRichard Lowe    output.write("Java style:\n")
2838bcea973SRichard Lowe    for f in flist(lambda x: x.endswith('.java')):
28410811751SAndy Fiddaman        with io.open(f, mode='rb') as fh:
2858bcea973SRichard Lowe            ret |= JStyle.jstyle(fh, output=output, picky=True)
2868bcea973SRichard Lowe    return ret
2878bcea973SRichard Lowe
28895c635efSGarrett D'Amoredef manlint(root, parent, flist, output):
28995c635efSGarrett D'Amore    ret = 0
29071af3be3SCody Peter Mello    output.write("Man page format/spelling:\n")
29195c635efSGarrett D'Amore    ManfileRE = re.compile(r'.*\.[0-9][a-z]*$', re.IGNORECASE)
29295c635efSGarrett D'Amore    for f in flist(lambda x: ManfileRE.match(x)):
293a90997d2SAndy Fiddaman        with io.open(f, mode='rb') as fh:
29495c635efSGarrett D'Amore            ret |= ManLint.manlint(fh, output=output, picky=True)
29571af3be3SCody Peter Mello            ret |= SpellCheck.spellcheck(fh, output=output)
29695c635efSGarrett D'Amore    return ret
29795c635efSGarrett D'Amore
298*13904da8SAndy Fiddamandef shelllint(root, parent, flist, output):
299*13904da8SAndy Fiddaman    ret = 0
300*13904da8SAndy Fiddaman    output.write("Shell lint:\n")
301*13904da8SAndy Fiddaman
302*13904da8SAndy Fiddaman    def isshell(x):
303*13904da8SAndy Fiddaman        (_, ext) = os.path.splitext(x)
304*13904da8SAndy Fiddaman        if ext in ['.sh', '.ksh']:
305*13904da8SAndy Fiddaman            return True
306*13904da8SAndy Fiddaman        if ext == '':
307*13904da8SAndy Fiddaman            with io.open(x, mode='r', errors='ignore') as fh:
308*13904da8SAndy Fiddaman                if re.match(r'^#.*\bk?sh\b', fh.readline()):
309*13904da8SAndy Fiddaman                    return True
310*13904da8SAndy Fiddaman        return False
311*13904da8SAndy Fiddaman
312*13904da8SAndy Fiddaman    for f in flist(isshell):
313*13904da8SAndy Fiddaman        with io.open(f, mode='rb') as fh:
314*13904da8SAndy Fiddaman            ret |= ShellLint.lint(fh, output=output)
315*13904da8SAndy Fiddaman
316*13904da8SAndy Fiddaman    return ret
317*13904da8SAndy Fiddaman
3188bcea973SRichard Lowedef keywords(root, parent, flist, output):
3198bcea973SRichard Lowe    ret = 0
3208bcea973SRichard Lowe    output.write("SCCS Keywords:\n")
3218bcea973SRichard Lowe    for f in flist():
322ca13eaa5SAndy Fiddaman        with io.open(f, encoding='utf-8', errors='replace') as fh:
3238bcea973SRichard Lowe            ret |= Keywords.keywords(fh, output=output)
3248bcea973SRichard Lowe    return ret
3258bcea973SRichard Lowe
3264ff15898SGordon Rossdef wscheck(root, parent, flist, output):
3274ff15898SGordon Ross    ret = 0
3284ff15898SGordon Ross    output.write("white space nits:\n")
3294ff15898SGordon Ross    for f in flist():
330ca13eaa5SAndy Fiddaman        with io.open(f, encoding='utf-8', errors='replace') as fh:
3314ff15898SGordon Ross            ret |= WsCheck.wscheck(fh, output=output)
3324ff15898SGordon Ross    return ret
3338bcea973SRichard Lowe
334955eb5e1SGarrett D'Amoredef symlinks(root, parent, flist, output):
335955eb5e1SGarrett D'Amore    ret = 0
336955eb5e1SGarrett D'Amore    output.write("Symbolic links:\n")
337955eb5e1SGarrett D'Amore    for f in flist():
338955eb5e1SGarrett D'Amore        output.write("  "+f+"\n")
339955eb5e1SGarrett D'Amore        ret |= 1
340955eb5e1SGarrett D'Amore    return ret
341955eb5e1SGarrett D'Amore
342955eb5e1SGarrett D'Amoredef iswinreserved(name):
343955eb5e1SGarrett D'Amore    reserved = [
344955eb5e1SGarrett D'Amore        'con', 'prn', 'aux', 'nul',
345955eb5e1SGarrett D'Amore        'com1', 'com2', 'com3', 'com4', 'com5',
346955eb5e1SGarrett D'Amore        'com6', 'com7', 'com8', 'com9', 'com0',
347955eb5e1SGarrett D'Amore        'lpt1', 'lpt2', 'lpt3', 'lpt4', 'lpt5',
348955eb5e1SGarrett D'Amore        'lpt6', 'lpt7', 'lpt8', 'lpt9', 'lpt0' ]
349955eb5e1SGarrett D'Amore    l = name.lower()
350955eb5e1SGarrett D'Amore    for r in reserved:
351955eb5e1SGarrett D'Amore        if l == r or l.startswith(r+"."):
352955eb5e1SGarrett D'Amore            return True
353955eb5e1SGarrett D'Amore    return False
354955eb5e1SGarrett D'Amore
355955eb5e1SGarrett D'Amoredef haswinspecial(name):
356955eb5e1SGarrett D'Amore    specials = '<>:"\\|?*'
357955eb5e1SGarrett D'Amore    for c in name:
358955eb5e1SGarrett D'Amore        if c in specials:
359955eb5e1SGarrett D'Amore            return True
360955eb5e1SGarrett D'Amore    return False
361955eb5e1SGarrett D'Amore
362955eb5e1SGarrett D'Amoredef winnames(root, parent, flist, output):
363955eb5e1SGarrett D'Amore    ret = 0
364955eb5e1SGarrett D'Amore    output.write("Illegal filenames (Windows):\n")
365955eb5e1SGarrett D'Amore    for f in flist():
366955eb5e1SGarrett D'Amore        if haswinspecial(f):
367955eb5e1SGarrett D'Amore            output.write("  "+f+": invalid character in name\n")
368955eb5e1SGarrett D'Amore            ret |= 1
369955eb5e1SGarrett D'Amore            continue
370955eb5e1SGarrett D'Amore
371955eb5e1SGarrett D'Amore        parts = f.split('/')
372955eb5e1SGarrett D'Amore        for p in parts:
373955eb5e1SGarrett D'Amore            if iswinreserved(p):
374955eb5e1SGarrett D'Amore                output.write("  "+f+": reserved file name\n")
375955eb5e1SGarrett D'Amore                ret |= 1
376955eb5e1SGarrett D'Amore                break
377955eb5e1SGarrett D'Amore
378955eb5e1SGarrett D'Amore    return ret
379955eb5e1SGarrett D'Amore
380955eb5e1SGarrett D'Amoredef run_checks(root, parent, cmds, scmds, paths='', opts={}):
3818bcea973SRichard Lowe    """Run the checks given in 'cmds', expected to have well-known signatures,
3828bcea973SRichard Lowe    and report results for any which fail.
3838bcea973SRichard Lowe
3848bcea973SRichard Lowe    Return failure if any of them did.
3858bcea973SRichard Lowe
3868bcea973SRichard Lowe    NB: the function name of the commands passed in is used to name the NOT
3878bcea973SRichard Lowe    file which excepts files from them."""
3888bcea973SRichard Lowe
3898bcea973SRichard Lowe    ret = 0
3908bcea973SRichard Lowe
3918bcea973SRichard Lowe    for cmd in cmds:
3928bcea973SRichard Lowe        s = StringIO()
3938bcea973SRichard Lowe
394ca13eaa5SAndy Fiddaman        exclude = not_check(root, cmd.__name__)
3958bcea973SRichard Lowe        result = cmd(root, parent, gen_files(root, parent, paths, exclude),
3968bcea973SRichard Lowe                     output=s)
3978bcea973SRichard Lowe        ret |= result
3988bcea973SRichard Lowe
3998bcea973SRichard Lowe        if result != 0:
400ca13eaa5SAndy Fiddaman            print(s.getvalue())
4018bcea973SRichard Lowe
402955eb5e1SGarrett D'Amore    for cmd in scmds:
403955eb5e1SGarrett D'Amore        s = StringIO()
404955eb5e1SGarrett D'Amore
405955eb5e1SGarrett D'Amore        exclude = not_check(root, cmd.__name__)
406955eb5e1SGarrett D'Amore        result = cmd(root, parent, gen_links(root, parent, paths, exclude),
407955eb5e1SGarrett D'Amore                     output=s)
408955eb5e1SGarrett D'Amore        ret |= result
409955eb5e1SGarrett D'Amore
410955eb5e1SGarrett D'Amore        if result != 0:
411955eb5e1SGarrett D'Amore            print(s.getvalue())
412955eb5e1SGarrett D'Amore
4138bcea973SRichard Lowe    return ret
4148bcea973SRichard Lowe
4158bcea973SRichard Lowedef nits(root, parent, paths):
4168bcea973SRichard Lowe    cmds = [copyright,
4178bcea973SRichard Lowe            cstyle,
4188bcea973SRichard Lowe            hdrchk,
4198bcea973SRichard Lowe            jstyle,
4208bcea973SRichard Lowe            keywords,
42195c635efSGarrett D'Amore            manlint,
4224ff15898SGordon Ross            mapfilechk,
423*13904da8SAndy Fiddaman            shelllint,
424955eb5e1SGarrett D'Amore            winnames,
4254ff15898SGordon Ross            wscheck]
426955eb5e1SGarrett D'Amore    scmds = [symlinks]
427955eb5e1SGarrett D'Amore    run_checks(root, parent, cmds, scmds, paths)
4288bcea973SRichard Lowe
4298bcea973SRichard Lowedef pbchk(root, parent, paths):
4308bcea973SRichard Lowe    cmds = [comchk,
4318bcea973SRichard Lowe            copyright,
4328bcea973SRichard Lowe            cstyle,
4338bcea973SRichard Lowe            hdrchk,
4348bcea973SRichard Lowe            jstyle,
4358bcea973SRichard Lowe            keywords,
43695c635efSGarrett D'Amore            manlint,
4374ff15898SGordon Ross            mapfilechk,
438*13904da8SAndy Fiddaman            shelllint,
439955eb5e1SGarrett D'Amore            winnames,
4404ff15898SGordon Ross            wscheck]
441955eb5e1SGarrett D'Amore    scmds = [symlinks]
442955eb5e1SGarrett D'Amore    run_checks(root, parent, cmds, scmds)
4438bcea973SRichard Lowe
4448bcea973SRichard Lowedef main(cmd, args):
4458bcea973SRichard Lowe    parent_branch = None
446eabe844aSJohn Levon    checkname = None
4478bcea973SRichard Lowe
4488bcea973SRichard Lowe    try:
44942a3762dSJoshua M. Clulow        opts, args = getopt.getopt(args, 'b:c:p:')
450ca13eaa5SAndy Fiddaman    except getopt.GetoptError as e:
4518bcea973SRichard Lowe        sys.stderr.write(str(e) + '\n')
452eabe844aSJohn Levon        sys.stderr.write("Usage: %s [-c check] [-p branch] [path...]\n" % cmd)
4538bcea973SRichard Lowe        sys.exit(1)
4548bcea973SRichard Lowe
4558bcea973SRichard Lowe    for opt, arg in opts:
45642a3762dSJoshua M. Clulow        # We accept "-b" as an alias of "-p" for backwards compatibility.
45742a3762dSJoshua M. Clulow        if opt == '-p' or opt == '-b':
4588bcea973SRichard Lowe            parent_branch = arg
459eabe844aSJohn Levon        elif opt == '-c':
460eabe844aSJohn Levon            checkname = arg
4618bcea973SRichard Lowe
4628bcea973SRichard Lowe    if not parent_branch:
4638bcea973SRichard Lowe        parent_branch = git_parent_branch(git_branch())
4648bcea973SRichard Lowe
465eabe844aSJohn Levon    if checkname is None:
4668bcea973SRichard Lowe        if cmd == 'git-pbchk':
467eabe844aSJohn Levon            checkname = 'pbchk'
468eabe844aSJohn Levon        else:
469eabe844aSJohn Levon            checkname = 'nits'
470eabe844aSJohn Levon
471eabe844aSJohn Levon    if checkname == 'pbchk':
4728bcea973SRichard Lowe        if args:
4738bcea973SRichard Lowe            sys.stderr.write("only complete workspaces may be pbchk'd\n");
4748bcea973SRichard Lowe            sys.exit(1)
475eabe844aSJohn Levon        pbchk(git_root(), parent_branch, None)
476eabe844aSJohn Levon    elif checkname == 'nits':
477eabe844aSJohn Levon        nits(git_root(), parent_branch, args)
478eabe844aSJohn Levon    else:
479eabe844aSJohn Levon        run_checks(git_root(), parent_branch, [eval(checkname)], args)
4808bcea973SRichard Lowe
4818bcea973SRichard Loweif __name__ == '__main__':
4828bcea973SRichard Lowe    try:
4838bcea973SRichard Lowe        main(os.path.basename(sys.argv[0]), sys.argv[1:])
484ca13eaa5SAndy Fiddaman    except GitError as e:
4858bcea973SRichard Lowe        sys.stderr.write("failed to run git:\n %s\n" % str(e))
4868bcea973SRichard Lowe        sys.exit(1)
487