xref: /freebsd/crypto/openssl/util/check-format-commit.sh (revision b64c5a0ace59af62eff52bfe110a521dc73c937b)
1#!/bin/bash
2# Copyright 2020-2024 The OpenSSL Project Authors. All Rights Reserved.
3#
4# Licensed under the Apache License 2.0 (the "License").
5# You may not use this file except in compliance with the License.
6# You can obtain a copy in the file LICENSE in the source distribution
7# or at https://www.openssl.org/source/license.html
8#
9# This script is a wrapper around check-format.pl.  It accepts a commit sha
10# value as input, and uses it to identify the files and ranges that were
11# changed in that commit, filtering check-format.pl output only to lines that
12# fall into the commits change ranges.
13#
14
15
16# List of Regexes to use when running check-format.pl.
17# Style checks don't apply to any of these
18EXCLUDED_FILE_REGEX=("\.pod" \
19                     "\.pl"  \
20                     "\.pm"  \
21                     "\.t"   \
22                     "\.yml" \
23                     "\.sh")
24
25# Exit code for the script
26EXIT_CODE=0
27
28# Global vars
29
30# TEMPDIR is used to hold any files this script creates
31# And is cleaned on EXIT with a trap function
32TEMPDIR=$(mktemp -d /tmp/checkformat.XXXXXX)
33
34# TOPDIR always points to the root of the git tree we are working in
35# used to locate the check-format.pl script
36TOPDIR=$(git rev-parse --show-toplevel)
37
38
39# cleanup handler function, returns us to the root of the git tree
40# and erases our temp directory
41cleanup() {
42    rm -rf $TEMPDIR
43    cd $TOPDIR
44}
45
46trap cleanup EXIT
47
48# Get the canonical sha256 sum for the commit we are checking
49# This lets us pass in symbolic ref names like master/etc and
50# resolve them to sha256 sums easily
51COMMIT=$(git rev-parse $1)
52
53# Fail gracefully if git rev-parse doesn't produce a valid
54# commit
55if [ $? -ne 0 ]
56then
57    echo "$1 is not a valid revision"
58    exit 1
59fi
60
61# Create a iteratable list of files to check for a
62# given commit. It produces output of the format
63# <commit id> <file name> <change start line>, <change line count>
64touch $TEMPDIR/ranges.txt
65git show $COMMIT | awk -v mycmt=$COMMIT '
66    BEGIN {myfile=""}
67    /+{3}/ {
68        gsub(/b\//,"",$2);
69        myfile=$2
70    }
71    /@@/ {
72        gsub(/+/,"",$3);
73        printf mycmt " " myfile " " $3 "\n"
74    }' >> $TEMPDIR/ranges.txt || true
75
76# filter out anything that matches on a filter regex
77for i in ${EXCLUDED_FILE_REGEX[@]}
78do
79    touch $TEMPDIR/ranges.filter
80    grep -v "$i" $TEMPDIR/ranges.txt >> $TEMPDIR/ranges.filter || true
81    REMAINING_FILES=$(wc -l $TEMPDIR/ranges.filter | awk '{print $1}')
82    if [ $REMAINING_FILES -eq 0 ]
83    then
84        echo "This commit has no files that require checking"
85        exit 0
86    fi
87    mv $TEMPDIR/ranges.filter $TEMPDIR/ranges.txt
88done
89
90# check out the files from the commit level.
91# For each file name in ranges, we show that file at the commit
92# level we are checking, and redirect it to the same path, relative
93# to $TEMPDIR/check-format.  This give us the full file to run
94# check-format.pl on with line numbers matching the ranges in the
95# $TEMPDIR/ranges.txt file
96for j in $(grep $COMMIT $TEMPDIR/ranges.txt | awk '{print $2}')
97do
98    FDIR=$(dirname $j)
99    mkdir -p $TEMPDIR/check-format/$FDIR
100    git show $COMMIT:$j > $TEMPDIR/check-format/$j
101done
102
103# Now for each file in $TEMPDIR/check-format run check-format.pl
104# Note that we use the %P formatter in the find utilty.  This strips
105# off the $TEMPDIR/check-format path prefix, leaving $j with the
106# path to the file relative to the root of the source dir, so that
107# output from check-format.pl looks correct, relative to the root
108# of the git tree.
109for j in $(find $TEMPDIR/check-format -type f -printf "%P\n")
110do
111    range_start=()
112    range_end=()
113
114    # Get the ranges for this file. Create 2 arrays.  range_start contains
115    # the start lines for valid ranges from the commit.  the range_end array
116    # contains the corresponding end line (note, since diff output gives us
117    # a line count for a change, the range_end[k] entry is actually
118    # range_start[k]+line count
119    for k in $(grep $COMMIT $TEMPDIR/ranges.txt | grep $j | awk '{print $3}')
120    do
121        RANGE=$k
122        RSTART=$(echo $RANGE | awk -F',' '{print $1}')
123        RLEN=$(echo $RANGE | awk -F',' '{print $2}')
124        let REND=$RSTART+$RLEN
125        range_start+=($RSTART)
126        range_end+=($REND)
127    done
128
129    # Go to our checked out tree
130    cd $TEMPDIR/check-format
131
132    # Actually run check-format.pl on the file, capturing the output
133    # in a temporary file.  Note the format of check-patch.pl output is
134    # <file name>:<line number>:<error text>:<offending line contents>
135    $TOPDIR/util/check-format.pl $j > $TEMPDIR/format-results.txt
136
137    # Now we filter the check-format.pl output based on the changed lines
138    # captured in the range_start/end arrays
139    let maxidx=${#range_start[@]}-1
140    for k in $(seq 0 1 $maxidx)
141    do
142        RSTART=${range_start[$k]}
143        REND=${range_end[$k]}
144
145        # field 2 of check-format.pl output is the offending line number
146        # Check here if any line in that output falls between any of the
147        # start/end ranges defined in the range_start/range_end array.
148        # If it does fall in that range, print the entire line to stdout
149        # If anything is printed, have awk exit with a non-zero exit code
150        awk -v rstart=$RSTART -v rend=$REND -F':' '
151                BEGIN {rc=0}
152                /:/ {
153                    if (($2 >= rstart) && ($2 <= rend)) {
154                        print $0;
155                        rc=1
156                    }
157                }
158                END {exit rc;}
159            ' $TEMPDIR/format-results.txt
160
161        # If awk exited with a non-zero code, this script will also exit
162        # with a non-zero code
163        if [ $? -ne 0 ]
164        then
165            EXIT_CODE=1
166        fi
167    done
168done
169
170# Exit with the recorded exit code above
171exit $EXIT_CODE
172