--- /dev/null
+#!/usr/bin/python
+#
+# This file is part of the GROMACS molecular simulation package.
+#
+# Copyright (c) 2013, by the GROMACS development team, led by
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
+#
+# GROMACS is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public License
+# as published by the Free Software Foundation; either version 2.1
+# of the License, or (at your option) any later version.
+#
+# GROMACS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with GROMACS; if not, see
+# http://www.gnu.org/licenses, or write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# If you want to redistribute modifications to GROMACS, please
+# consider that scientific software is very special. Version
+# control is crucial - bugs must be traceable. We will be happy to
+# consider code for inclusion in the official distribution, but
+# derived work must not be called official GROMACS. Details are found
+# in the README & COPYING files - if they are missing, get the
+# official version at http://www.gromacs.org.
+#
+# To help us fund GROMACS development, we humbly ask that you cite
+# the research papers on the package. Check out http://www.gromacs.org.
+
+import datetime
+import os.path
+import re
+import sys
+
+from optparse import OptionParser
+
+class CopyrightState(object):
+
+ """Information about an existing (or non-existing) copyright header."""
+
+ def __init__(self, has_copyright, is_correct, is_newstyle, years, other_copyrights):
+ self.has_copyright = has_copyright
+ self.is_correct = is_correct
+ self.is_newstyle = is_newstyle
+ self.years = years
+ self.other_copyrights = other_copyrights
+
+class CopyrightChecker(object):
+
+ """Logic for analyzing existing copyright headers and generating new ones."""
+
+ _header = ["", "This file is part of the GROMACS molecular simulation package.", ""]
+ _copyright = "Copyright (c) {0}, by the GROMACS development team, led by"
+ _footer = """
+Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+and including many others, as listed in the AUTHORS file in the
+top-level source directory and at http://www.gromacs.org.
+
+GROMACS is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public License
+as published by the Free Software Foundation; either version 2.1
+of the License, or (at your option) any later version.
+
+GROMACS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with GROMACS; if not, see
+http://www.gnu.org/licenses, or write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+If you want to redistribute modifications to GROMACS, please
+consider that scientific software is very special. Version
+control is crucial - bugs must be traceable. We will be happy to
+consider code for inclusion in the official distribution, but
+derived work must not be called official GROMACS. Details are found
+in the README & COPYING files - if they are missing, get the
+official version at http://www.gromacs.org.
+
+To help us fund GROMACS development, we humbly ask that you cite
+the research papers on the package. Check out http://www.gromacs.org.
+""".strip().splitlines()
+
+ def check_copyright(self, comment_block):
+ """Analyze existing copyright header for correctness and extract information."""
+ copyright_re = r'Copyright \(c\) (([0-9]{4}[,-])*[0-9]{4}),? by the GROMACS development team'
+ has_copyright = False
+ is_newstyle = True
+ is_correct = True
+ next_header_line = 0
+ next_footer_line = 0
+ existing_years = ''
+ other_copyrights = []
+ for line in comment_block:
+ if 'Copyright' in line:
+ has_copyright = True
+ match = re.match(copyright_re, line)
+ if match:
+ existing_years = match.group(1)
+ new_line = self._copyright.format(existing_years)
+ if line != new_line:
+ is_correct = False
+ else:
+ other_copyrights.append(line)
+ if next_header_line != -1 or next_footer_line != 0:
+ is_correct = False
+ continue
+ if line.startswith('Written by the Gromacs development team'):
+ has_copyright = True
+ if next_header_line >= 0:
+ if line == self._header[next_header_line]:
+ next_header_line += 1
+ if next_header_line >= len(self._header):
+ next_header_line = -1
+ else:
+ is_correct = False
+ is_newstyle = False
+ elif next_footer_line >= 0:
+ if line == self._footer[next_footer_line]:
+ next_footer_line += 1
+ if next_footer_line >= len(self._footer):
+ next_footer_line = -1
+ else:
+ is_correct = False
+ else:
+ is_correct = False
+ if next_header_line != -1 or next_footer_line != -1:
+ is_correct = False
+
+ return CopyrightState(has_copyright, is_correct, is_newstyle, existing_years, other_copyrights)
+
+ def process_copyright(self, state, options, current_years, reporter):
+ """Determine whether a copyrigth header needs to be updated and report issues."""
+ need_update = False
+
+ if state.years:
+ if options.replace_years:
+ if state.years != current_years:
+ need_update = True
+ reporter.report('copyright years replaced')
+ new_years = current_years
+ else:
+ new_years = state.years
+ if not new_years.endswith(current_years):
+ if options.update_year:
+ need_update = True
+ new_years += ',' + current_years
+ if options.check or not need_update:
+ reporter.report('copyright year outdated')
+ else:
+ reporter.report('copyright year added')
+ else:
+ new_years = current_years
+
+ if not state.has_copyright:
+ if options.add_missing:
+ need_update = True
+ if options.check or not need_update:
+ reporter.report('copyright header missing')
+ elif options.add_missing:
+ reporter.report('copyright header added')
+ else:
+ if not state.is_newstyle:
+ if options.replace_header:
+ need_update = True
+ if options.check or not need_update:
+ reporter.report('copyright header incorrect')
+ else:
+ reporter.report('copyright header replaced')
+ elif not state.is_correct:
+ if options.update_header:
+ need_update = True
+ if options.check or not need_update:
+ reporter.report('copyright header outdated')
+ else:
+ reporter.report('copyright header updated')
+
+ return need_update, new_years
+
+ def get_copyright_text(self, years, other_copyrights):
+ """Construct a new copyright header."""
+ output = []
+ output.extend(self._header)
+ if other_copyrights:
+ for line in other_copyrights:
+ outline = line.rstrip()
+ if outline.endswith(','):
+ outline = outline[:-1]
+ if not outline.endswith('.'):
+ outline += '.'
+ output.append(outline)
+ output.append(self._copyright.format(years))
+ output.extend(self._footer)
+ return output
+
+class Reporter(object):
+
+ """Wrapper for reporting issues in a file."""
+
+ def __init__(self, reportfile, filename):
+ self._reportfile = reportfile
+ self._filename = filename
+
+ def report(self, text):
+ self._reportfile.write(self._filename + ': ' + text + '\n');
+
+class CommentHandlerC(object):
+
+ """Handler for extracting and creating C-style comments."""
+
+ def extract_first_comment_block(self, content_lines):
+ if not content_lines or not content_lines[0].startswith('/*'):
+ return ([], 0)
+ comment_block = [content_lines[0][2:].strip()]
+ line_index = 1
+ while line_index < len(content_lines):
+ line = content_lines[line_index]
+ if '*/' in content_lines[line_index]:
+ break
+ comment_block.append(line.lstrip('* ').rstrip())
+ line_index += 1
+ return (comment_block, line_index + 1)
+
+ def create_comment_block(self, lines):
+ output = []
+ output.append(('/* ' + lines[0]).rstrip())
+ output.extend([(' * ' + x).rstrip() for x in lines[1:]])
+ output.append(' */')
+ return output
+
+class CommentHandlerSh(object):
+
+ """Handler for extracting and creating sh-style comments."""
+
+ def extract_first_comment_block(self, content_lines):
+ if not content_lines or not content_lines[0].startswith('#'):
+ return ([], 0)
+ comment_block = []
+ line_index = 0
+ while line_index < len(content_lines):
+ line = content_lines[line_index]
+ if not line.startswith('#'):
+ break
+ comment_block.append(line.lstrip('# ').rstrip())
+ line_index += 1
+ if line == '# the research papers on the package. Check out http://www.gromacs.org.':
+ break
+ while line_index < len(content_lines):
+ line = content_lines[line_index].rstrip()
+ if len(line) > 0 and line != '#':
+ break
+ line_index += 1
+ return (comment_block, line_index)
+
+ def create_comment_block(self, lines):
+ output = []
+ output.extend([('# ' + x).rstrip() for x in lines])
+ output.append('')
+ return output
+
+comment_handlers = {'c': CommentHandlerC(), 'sh': CommentHandlerSh()}
+
+def select_comment_handler(override, filename):
+ """Select comment handler for a file based on file name and input options."""
+ filetype = override
+ if not filetype and filename != '-':
+ basename = os.path.basename(filename)
+ root, ext = os.path.splitext(basename)
+ if ext == '.cmakein':
+ dummy, ext2 = os.path.splitext(root)
+ if ext2:
+ ext = ext2
+ if ext in ('.c', '.cpp', '.h', '.y', '.l', '.pre'):
+ filetype = 'c'
+ elif basename in ('CMakeLists.txt', 'GMXRC', 'git-pre-commit') or \
+ ext in ('.cmake', '.cmakein', '.py', '.sh', '.bash', '.csh', '.zsh'):
+ filetype = 'sh'
+ if filetype in comment_handlers:
+ return comment_handlers[filetype]
+ if filetype:
+ sys.stderr.write("Unsupported input format: {0}\n".format(filetype))
+ elif filename != '-':
+ sys.stderr.write("Unsupported input format: {0}\n".format(filename))
+ else:
+ sys.stderr.write("No file name or file type provided.\n")
+ sys.exit(1)
+
+def create_copyright_header(years, other_copyrights=None, language='c'):
+ if language not in comment_handlers:
+ sys.strerr.write("Unsupported language: {0}\n".format(language))
+ sys.exit(1)
+ copyright_checker = CopyrightChecker()
+ comment_handler = comment_handlers[language]
+ copyright_lines = copyright_checker.get_copyright_text(years, other_copyrights)
+ comment_lines = comment_handler.create_comment_block(copyright_lines)
+ return '\n'.join(comment_lines) + '\n'
+
+def process_options():
+ """Process input options."""
+ parser = OptionParser()
+ parser.add_option('-l', '--lang',
+ help='Comment type to use (c or sh)')
+ parser.add_option('-y', '--years',
+ help='Comma-separated list of years')
+ parser.add_option('-F', '--files',
+ help='File to read list of files from')
+ parser.add_option('--check', action='store_true',
+ help='Do not modify the files, only check the copyright (default action). ' +
+ 'If specified together with --update, do the modifications ' +
+ 'but produce output as if only --check was provided.')
+ parser.add_option('--update-year', action='store_true',
+ help='Update the copyright year if outdated')
+ parser.add_option('--replace-years', action='store_true',
+ help='Replace the copyright years with those given with --years')
+ parser.add_option('--update-header', action='store_true',
+ help='Update the copyright header if outdated')
+ parser.add_option('--replace-header', action='store_true',
+ help='Replace any copyright header with the current one')
+ parser.add_option('--add-missing', action='store_true',
+ help='Add missing copyright headers')
+ options, args = parser.parse_args()
+
+ filenames = args
+ if options.files:
+ with open(options.files, 'r') as filelist:
+ filenames = [x.strip() for x in filelist.read().splitlines()]
+ elif not filenames:
+ filenames = ['-']
+
+ # Default is --check if nothing provided.
+ if not options.check and not options.update_year and \
+ not options.update_header and not options.replace_header and \
+ not options.add_missing:
+ options.check = True
+
+ return options, filenames
+
+def main():
+ """Do processing as a stand-alone script."""
+ options, filenames = process_options()
+ years = options.years
+ if not years:
+ years = str(datetime.date.today().year)
+ if years.endswith(','):
+ years = years[:-1]
+
+ checker = CopyrightChecker()
+
+ # Process each input file in turn.
+ for filename in filenames:
+ comment_handler = select_comment_handler(options.lang, filename)
+
+ # Read the input file. We are doing an in-place operation, so can't
+ # operate in pass-through mode.
+ if filename == '-':
+ contents = sys.stdin.read().splitlines()
+ reporter = Reporter(sys.stderr, '<stdin>')
+ else:
+ with open(filename, 'r') as inputfile:
+ contents = inputfile.read().splitlines()
+ reporter = Reporter(sys.stdout, filename)
+
+ output = []
+ # Keep lines that must be at the beginning of the file and skip them in
+ # the check.
+ if contents and (contents[0].startswith('#!/') or \
+ contents[0].startswith('%code requires') or \
+ contents[0].startswith('/* #if')):
+ output.append(contents[0])
+ contents = contents[1:]
+ # Remove and skip empty lines at the beginning.
+ while contents and len(contents[0]) == 0:
+ contents = contents[1:]
+
+ # Analyze the first comment block in the file.
+ comment_block, line_count = comment_handler.extract_first_comment_block(contents)
+ state = checker.check_copyright(comment_block)
+ need_update, file_years = checker.process_copyright(state, options, years, reporter)
+
+ if need_update:
+ # Remove the original comment if it was a copyright comment.
+ if state.has_copyright:
+ contents = contents[line_count:]
+ new_block = checker.get_copyright_text(file_years, state.other_copyrights)
+ output.extend(comment_handler.create_comment_block(new_block))
+
+ # Write the output file if required.
+ if need_update or filename == '-':
+ # Append the rest of the input file as it was.
+ output.extend(contents)
+ output = '\n'.join(output) + '\n'
+ if filename == '-':
+ sys.stdout.write(output)
+ else:
+ with open(filename, 'w') as outputfile:
+ outputfile.write(output)
+
+if __name__ == "__main__":
+ main()
# the research papers on the package. Check out http://www.gromacs.org.
# This script is intended as a pre-commit hook that optionally runs all
-# changes through uncrustify. By default, it does nothing. To enable the
-# script after copying it to .git/hooks/pre-commit, you need to set
+# changes through some formatting check. Currently, it runs uncrustify and
+# checks copyright headers.
+#
+# By default, it does nothing. To enable the script after copying it to
+# .git/hooks/pre-commit, you need to set
# git config hooks.uncrustifymode check
# git config hooks.uncrustifypath /path/to/uncrustify
# With this configuration, all source files modified in the commit are run
-# through uncrustify (see admin/uncrustify.sh for how this set of files is
-# determined). If any file is changed by uncrustify, the names of those files
-# are reported and the commit is prevented.
+# through uncrustify and checked for correct copyright headers
+# (see admin/uncrustify.sh for how this set of files is determined).
+# If any file is changed by uncrustify or has other problems, the names of
+# those files are reported and the commit is prevented. The issues can be
+# fixed by running admin/uncrustify.sh manually.
+#
# To disable the hook, you can set
# git config hooks.uncrustifymode off
+# To disable it temporarily for a commit, set NO_FORMAT_CHECK environment
+# variable. For example,
+# NO_FORMAT_CHECK=1 git commit -a
+# You can also run git commit --no-verify, but that also disables other hooks,
+# such as the Change-Id hook used by gerrit.
#
# The actual work is done by the admin/uncrustify.sh script, which gets
# run with the 'check-index' action.
# See the comments in that script for more information.
+if [ ! -z "$NO_FORMAT_CHECK" ]
+then
+ exit 0
+fi
+
if git rev-parse --verify HEAD >/dev/null 2>&1
then
- against=HEAD
+ against=HEAD
else
- # Initial commit: diff against an empty tree object
- against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+ # Initial commit: diff against an empty tree object
+ against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
# Redirect output to stderr.
exec 1>&2
uncrustify_mode=`git config hooks.uncrustifymode`
+copyright_mode=`git config hooks.copyrightmode`
+if [ -z "$uncrustify_mode" ]
+then
+ uncrustify_mode=off
+fi
+if [ -z "$copyright_mode" ]
+then
+ copyright_mode=off
+fi
-if [ -f admin/uncrustify.sh ] && \
- [ ! -z "$uncrustify_mode" ] && [ "$uncrustify_mode" != "off" ]
+if [[ -f admin/uncrustify.sh && \
+ ( "$uncrustify_mode" != "off" || "$copyright_mode" != "off" ) ]]
then
- uncrustify_path=`git config hooks.uncrustifypath`
- if [ -z "$uncrustify_path" ]
+ if [ "$uncrustify_mode" != "off" ]
then
- echo "Please set the path to uncrustify using 'git config hooks.uncrustifypath'."
- echo "Note that you need a custom version of uncrustify."
+ uncrustify_path=`git config hooks.uncrustifypath`
+ if [ -z "$uncrustify_path" ]
+ then
+ echo "Please set the path to uncrustify using 'git config hooks.uncrustifypath'."
+ echo "Note that you need a custom version of uncrustify."
+ exit 1
+ fi
+ export UNCRUSTIFY="$uncrustify_path"
+ fi
+ admin/uncrustify.sh check-index --rev=$against \
+ --uncrustify="$uncrustify_mode" --copyright="$copyright_mode"
+ stat=$?
+ if [ $stat -eq 1 ] ; then
+ exit 1
+ elif [ $stat -ne 0 ] ; then
+ echo "Source code formatting check failed"
exit 1
fi
- export UNCRUSTIFY="$uncrustify_path"
- case "$uncrustify_mode" in
- check)
- admin/uncrustify.sh check-index --rev=$against
- stat=$?
- if [ $stat -eq 1 ] ; then
- exit 1
- elif [ $stat -ne 0 ] ; then
- echo "Source code formatting check failed"
- exit 1
- fi
- ;;
- *)
- echo "Unknown uncrustify mode: $uncrustify_mode"
- exit 1
- ;;
- esac
fi
# This file is part of the GROMACS molecular simulation package.
#
# Copyright (c) 2013, by the GROMACS development team, led by
-# David van der Spoel, Berk Hess, Erik Lindahl, and including many
-# others, as listed in the AUTHORS file in the top-level source
-# directory and at http://www.gromacs.org.
+# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+# and including many others, as listed in the AUTHORS file in the
+# top-level source directory and at http://www.gromacs.org.
#
# GROMACS is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# the research papers on the package. Check out http://www.gromacs.org.
# This script runs uncrustify on modified files and reports/applies the
-# results. By default, the current HEAD commit is compared to the work tree,
+# results. It also checks for outdated copyright years or headers.
+# By default, the current HEAD commit is compared to the work tree,
# and files that
# 1. are different between these two trees and
-# 2. change under uncrustify
+# 2. change under uncrustify and/or have outdated copyright header
# are reported. This behavior can be changed by
# 1. Specifying an --rev=REV argument, which uses REV instead of HEAD as
# the base of the comparison.
# 2. Specifying an action:
-# check-* reports the files that uncrustify changes
-# diff-* prints the actual diff of what would change
-# update-* applies the changes to the repository
-# *-workdir operates on the working directory (files on disk)
-# *-index operates on the index of the repository
+# check-*: reports the files that uncrustify changes
+# diff-*: prints the actual diff of what would change
+# update-*: applies the changes to the repository
+# *-workdir: operates on the working directory (files on disk)
+# *-index: operates on the index of the repository
# For convenience, if you omit the workdir/index suffix, workdir is assumed
# (i.e., diff equals diff-workdir).
+# 3. Specifying --uncrustify=off, which does not run uncrustify.
+# 4. Specifying --copyright=<mode>, which alters the level of copyright
+# checking is done:
+# off: does not check copyright headers at all
+# year: only update copyright year in new-format copyright headers
+# add: in addition to 'year', add copyright headers to files that
+# don't have any
+# update: in addition to 'year' and 'add', also update new-format
+# copyright headers if they are broken or outdated.
+# replace: replace any copyright header with a new-format copyright
+# header
+# full: do all of the above
# By default, update-* refuses to update "dirty" files (i.e., that differ
# between the disk and the index) to make it easy to revert the changes.
# This can be overridden by adding a -f/--force option.
# To identify which files to run through uncrustify, the script uses git
# filters, specified in .gitattributes files. Only files that have the filter
# set as "uncrustify" (or something ending in "uncrustify") are processed: if
-# other files have been changed, they are ignored by the script.
+# other files have been changed, they are ignored by the script. Files passed
+# to uncrustify, as well as files with filter "copyright", get their copyright
+# header checked.
#
# If you want to run uncrustify automatically for changes you make, there are
# two options:
# The pre-commit hook + manually running the script gives better/more intuitive
# control (with the filter, it is possible to have a work tree that is
# different from HEAD and still have an empty 'git diff') and provides better
-# performance for changes that modify many files.
+# performance for changes that modify many files. It is the only way that
+# currently also checks the copyright headers.
# The filter allows one to transparently merge branches that have not been run
# through uncrustify, and is applied more consistently (the pre-commit hook is
# not run for every commit, e.g., during a rebase).
# Parse command-line arguments
function usage() {
- echo "usage: uncrustify.sh [-f|--force] [--rev=REV] [action]"
- echo "actions: (check|diff|update)[-(index|workdir)] (default:check-workdir)"
+ echo "usage: uncrustify.sh [-f|--force] [--rev=REV]"
+ echo " [--uncrustify=(off|check)] [--copyright=<cmode>] [<action>]"
+ echo "<action>: (check*|diff|update)[-(index|workdir*)] (*=default)"
+ echo "<cmode>: off|add|update*|replace|full"
}
action="check-workdir"
declare -a diffargs
baserev="HEAD"
force=
+uncrustify_mode=check
+copyright_mode=update
for arg in "$@" ; do
if [[ "$arg" == "check-index" || "$arg" == "check-workdir" || \
"$arg" == "diff-index" || "$arg" == "diff-workdir" || \
action=$arg-workdir
elif [[ "$action" == diff-* ]] ; then
diffargs+=("$arg")
+ elif [[ "$arg" == --uncrustify=* ]] ; then
+ uncrustify_mode=${arg#--uncrustify=}
+ if [[ "$uncrustify_mode" != "off" && "$uncrustify_mode" != "check" ]] ; then
+ echo "Unknown option: $arg"
+ echo
+ usage
+ exit 2
+ fi
+ elif [[ "$arg" == --copyright=* ]] ; then
+ copyright_mode=${arg#--copyright=}
elif [[ "$arg" == "-f" || "$arg" == "--force" ]] ; then
force=1
elif [[ "$arg" == --rev=* ]] ; then
done
# Check that uncrustify is present
-if [ -z "$UNCRUSTIFY" ]
+if [[ "$uncrustify_mode" != "off" ]]
then
- echo "Please set the path to uncrustify using UNCRUSTIFY."
- echo "Note that you need a custom version of uncrustify."
- echo "See comments in the script file for how to get one."
- exit 2
-fi
-if ! which "$UNCRUSTIFY" 1>/dev/null
-then
- echo "Uncrustify not found: $UNCRUSTIFY"
- exit 2
+ if [ -z "$UNCRUSTIFY" ]
+ then
+ echo "Please set the path to uncrustify using UNCRUSTIFY."
+ echo "Note that you need a custom version of uncrustify."
+ echo "See comments in the script file for how to get one."
+ exit 2
+ fi
+ if ! which "$UNCRUSTIFY" 1>/dev/null
+ then
+ echo "Uncrustify not found: $UNCRUSTIFY"
+ exit 2
+ fi
fi
# Switch to the root of the source tree and check the config file
tmpdir=`mktemp -d -t gmxuncrust.XXXXXX`
# Produce a list of changed files
-# Only include files that have uncrustify set as filter in .gitattributes
+# Only include files that have proper filter set in .gitattributes
internal_diff_args=
if [[ $action == *-index ]]
then
git check-attr --stdin filter | \
sed -e 's/.*: filter: //' | \
paste $tmpdir/difflist - | \
- grep 'uncrustify$' >$tmpdir/filtered
-cut -f2 <$tmpdir/filtered >$tmpdir/filelist
-git diff-files --name-only | grep -Ff $tmpdir/filelist >$tmpdir/localmods
+ grep -E '(uncrustify|copyright)$' >$tmpdir/filtered
+cut -f2 <$tmpdir/filtered >$tmpdir/filelist_all
+grep 'uncrustify$' <$tmpdir/filtered | cut -f2 >$tmpdir/filelist_uncrustify
+git diff-files --name-only | grep -Ff $tmpdir/filelist_all >$tmpdir/localmods
# Extract changed files to a temporary directory
mkdir $tmpdir/org
-mkdir $tmpdir/new
if [[ $action == *-index ]] ; then
- git checkout-index --prefix=$tmpdir/org/ --stdin <$tmpdir/filelist
+ git checkout-index --prefix=$tmpdir/org/ --stdin <$tmpdir/filelist_all
else
- rsync --files-from=$tmpdir/filelist $srcdir $tmpdir/org
+ rsync --files-from=$tmpdir/filelist_all $srcdir $tmpdir/org
fi
+# Duplicate the original files to a separate directory, where all changes will
+# be made.
+cp -r $tmpdir/org $tmpdir/new
+
+# Create output file for what was done (in case no messages get written)
+touch $tmpdir/messages
# Run uncrustify on the temporary directory
-cd $tmpdir/org
+cd $tmpdir/new
+if [[ $uncrustify_mode != "off" ]] ; then
+ if ! $UNCRUSTIFY -c $cfg_file -F $tmpdir/filelist_uncrustify --no-backup >$tmpdir/uncrustify.out 2>&1 ; then
+ echo "Reformatting failed. Check uncrustify output below for errors:"
+ cat $tmpdir/uncrustify.out
+ rm -rf $tmpdir
+ exit 2
+ fi
+ # Find the changed files if necessary
+ if [[ $action != diff-* ]] ; then
+ msg="needs uncrustify"
+ if [[ $action == update-* ]] ; then
+ msg="uncrustified"
+ fi
+ git diff --no-index --name-only ../org/ . | \
+ awk -v msg="$msg" '{sub(/.\//,""); print $0 ": " msg}' >> $tmpdir/messages
+ fi
+ # TODO: Consider checking whether rerunning uncrustify causes additional changes
+fi
-if ! $UNCRUSTIFY -c $cfg_file -F $tmpdir/filelist --prefix=../new/ >$tmpdir/uncrustify.out 2>&1 ; then
- echo "Reformatting failed. Check uncrustify output below for errors:"
- cat $tmpdir/uncrustify.out
- rm -rf $tmpdir
- exit 2
+# Update the copyright headers using the requested mode
+if [[ $copyright_mode != "off" ]] ; then
+ cpscript_args="--update-year"
+ case "$copyright_mode" in
+ year)
+ ;;
+ add)
+ cpscript_args+=" --add-missing"
+ ;;
+ update)
+ cpscript_args+=" --add-missing --update-header"
+ ;;
+ replace)
+ cpscript_args+=" --replace-header"
+ ;;
+ full)
+ cpscript_args+=" --add-missing --update-header --replace-header"
+ ;;
+ *)
+ echo "Unknown copyright mode: $copyright_mode"
+ exit 2
+ if [[ $copyright -eq 2 ]] ; then
+ cpscript_args+=" --update-header"
+ fi
+ esac
+ if [[ $action == check-* ]] ; then
+ cpscript_args+=" --check"
+ fi
+ # TODO: Probably better to invoke python explicitly through a customizable
+ # variable.
+ if ! $admin_dir/copyright.py -F $tmpdir/filelist_all $cpscript_args >>$tmpdir/messages
+ then
+ echo "Copyright checking failed!"
+ rm -rf $tmpdir
+ exit 2
+ fi
fi
-# TODO: Consider checking whether rerunning uncrustify causes additional changes
cd $tmpdir
fi
# Find the changed files
-touch $tmpdir/messages
changes=
set -o pipefail
if ! git diff --no-index --name-only --exit-code org/ new/ | \
sed -e 's#new/##' > $tmpdir/changed
then
changes=1
- awk '{print $0 ": needs uncrustify"}' $tmpdir/changed \
- >> $tmpdir/messages
fi
+
# Check if changed files have changed outside the index
if grep -Ff $tmpdir/localmods $tmpdir/changed > $tmpdir/conflicts
then
awk '{print $0 ": has changes in work tree"}' $tmpdir/conflicts \
>> $tmpdir/messages
if [[ ! $force && $action == update-* ]] ; then
+ echo "Modified files found in work tree, skipping update. Use -f to override."
+ echo "The following would have been done:"
sort $tmpdir/messages
- echo "Modified files found in work tree, skipping update."
rm -rf $tmpdir
exit 2
fi
fi
# Report what was done
-if [[ $action == update-* ]] ; then
- sort $tmpdir/messages | sed -e 's/needs uncrustify/uncrustified/'
-else
- sort $tmpdir/messages
-fi
+sort $tmpdir/messages
rm -rf $tmpdir
exit $changes