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