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