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