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