diff --git a/git_hooks/Makefile b/git_hooks/Makefile new file mode 100644 index 000000000..d35b8e50e --- /dev/null +++ b/git_hooks/Makefile @@ -0,0 +1,16 @@ +#---------------------------------------------------------------------------# +# vim: ts=8 sw=8 noexpandtab +#---------------------------------------------------------------------------# +# +# A makefile for the update_copyright program. +# +# This Makefile is designed for manual operation by Mercury developers; +# it is *not* designed to be invoked by any action in any other Makefile +# or Mmakefile, in any other part of that workspace. +# + +update_copyright: update_copyright.m + mmc --mercury-linkage static update_copyright.m + +clean: + git clean -fx 'update_copyright.*' 'update_copyright_init.*' diff --git a/git_hooks/update_copyright.m b/git_hooks/update_copyright.m index 229076c23..a63eed04b 100644 --- a/git_hooks/update_copyright.m +++ b/git_hooks/update_copyright.m @@ -208,9 +208,9 @@ read_lines_loop(InputStream, Options, CurrentYear, !.ModState = unmodified, parse_copyright_line(Options, Line, Prefix, Ranges0, Suffix) then - % We could normalise ranges here. - sort(Ranges0, Ranges1), - ( if add_to_ranges(CurrentYear, Ranges1, Ranges) then + list.sort(Ranges0, Ranges1), + merge_adjacent_ranges_if_possible(Ranges1, Ranges2), + ( if add_to_ranges(CurrentYear, Ranges2, Ranges) then make_copyright_line(Prefix, Ranges, Suffix, NewLine), !:RevLines = [NewLine | !.RevLines], !:ModState = found_modified @@ -289,6 +289,13 @@ parse_ranges(Str, Ranges) :- Words = string.words_separator(is_whitespace_or_comma, Str), list.map(parse_range, Words, Ranges). +:- pred is_whitespace_or_comma(char::in) is semidet. + +is_whitespace_or_comma(C) :- + ( char.is_whitespace(C) + ; C = (',') + ). + :- pred parse_range(string::in, year_range::out) is semidet. parse_range(Str, Range) :- @@ -305,11 +312,27 @@ parse_range(Str, Range) :- Range = years(N1, N2) ). -:- pred is_whitespace_or_comma(char::in) is semidet. +:- pred merge_adjacent_ranges_if_possible( + list(year_range)::in, list(year_range)::out) is det. -is_whitespace_or_comma(C) :- - ( char.is_whitespace(C) - ; C = (',') +merge_adjacent_ranges_if_possible(Ranges0, Ranges) :- + ( + ( Ranges0 = [] + ; Ranges0 = [_] + ), + Ranges = Ranges0 + ; + Ranges0 = [Range1, Range2 | Ranges3plus], + Range1 = years(RangeLo1, RangeHi1), + Range2 = years(RangeLo2, RangeHi2), + ( if RangeLo2 =< RangeHi1 + 1 then + Range12 = years(RangeLo1, RangeHi2), + merge_adjacent_ranges_if_possible([Range12 | Ranges3plus], Ranges) + else + merge_adjacent_ranges_if_possible([Range2 | Ranges3plus], + TailRanges), + Ranges = [Range1 | TailRanges] + ) ). :- pred add_to_ranges(int::in, list(year_range)::in, list(year_range)::out) diff --git a/git_hooks/update_copyright.pre-commit b/git_hooks/update_copyright.pre-commit index 5b6bcf161..e1d965c34 100755 --- a/git_hooks/update_copyright.pre-commit +++ b/git_hooks/update_copyright.pre-commit @@ -1,26 +1,46 @@ #!/usr/bin/env bash # -# You can copy/symlink this script to .git/hooks/pre-commit. -# It will update the Copyright lines of all files that are going to be -# modified in the commit, before the commit is made. +# This script is designed to update the Copyright lines of all files +# that are going to be modified in the commit in a workspace, +# before the commit is made. # -# You need to compile the update_copyright.m program: -# mmc --mercury-linkage static update_copyright.m +# If ${ws} is the pathname of a workspace, then you enable this script +# for that workspace by # -# You can clean the intermediate files with: -# git clean -fx 'update_copyright.*' 'update_copyright_init.*' +# - compiling update_copyright.m to an executable, and +# - copying or symlinking this script to ${ws}/.git/hooks/pre-commit. # +# The first step can be done by the command +# +# make update_copyright +# +# The unneeded intermediate files from the first step can cleaned up +# by the command +# +# make clean +# +# By default, this script requires the executable git_hooks/update_copyright +# to be present in the workspace. You can avoid having a duplicate copy +# of that executable in every workspace by +# +# - putting that executable in some central location, and +# - setting the environment variable UPDATE_MERCURY_COPYRIGHT +# to invoke that executable. +# + set -e rootdir=$( git rev-parse --show-toplevel ) -update_copyright="$rootdir/git_hooks/update_copyright" - -# Only continue if the update_copyright program has been compiled. -if test ! -x "$update_copyright" +if test "${UPDATE_MERCURY_COPYRIGHT}" != "" then - exit + update_copyright="${UPDATE_MERCURY_COPYRIGHT}" +else + update_copyright="${rootdir}/git_hooks/update_copyright" fi +# Continue only if the update_copyright program is available. +command -v "${update_copyright}" > /dev/null || exit 0 + # Find changed files for this commit. changed_files=($( git diff --name-only --cached --diff-filter=ACMR )) update_files=() @@ -28,7 +48,7 @@ unknown_files=() for file in "${changed_files[@]}" do - case $file in + case ${file} in *.[mchly] | *.in | *.java | *Mmake*) update_files+=( "$file" ) ;; @@ -47,5 +67,5 @@ fi if test "${#update_files}" -gt 0 then # Accept both "Mercury team" and "Mercury Team". - exec "$update_copyright" --suffix ' Mercury ' -- "${update_files[@]}" + exec "${update_copyright}" --suffix ' Mercury ' -- "${update_files[@]}" fi