1#!/usr/bin/env bash 2# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. 3# If clang_format_diff.py command is not specfied, we assume we are able to 4# access directly without any path. 5 6print_usage () { 7 echo "Usage:" 8 echo "format-diff.sh [OPTIONS]" 9 echo "-c: check only." 10 echo "-h: print this message." 11} 12 13while getopts ':ch' OPTION; do 14 case "$OPTION" in 15 c) 16 CHECK_ONLY=1 17 ;; 18 h) 19 print_usage 20 exit 1 21 ;; 22 ?) 23 print_usage 24 exit 1 25 ;; 26 esac 27done 28 29REPO_ROOT="$(git rev-parse --show-toplevel)" 30 31if [ "$CLANG_FORMAT_DIFF" ]; then 32 echo "Note: CLANG_FORMAT_DIFF='$CLANG_FORMAT_DIFF'" 33 # Dry run to confirm dependencies like argparse 34 if $CLANG_FORMAT_DIFF --help >/dev/null < /dev/null; then 35 true #Good 36 else 37 exit 128 38 fi 39else 40 # First try directly executing the possibilities 41 if clang-format-diff.py --help &> /dev/null < /dev/null; then 42 CLANG_FORMAT_DIFF=clang-format-diff.py 43 elif $REPO_ROOT/clang-format-diff.py --help &> /dev/null < /dev/null; then 44 CLANG_FORMAT_DIFF=$REPO_ROOT/clang-format-diff.py 45 else 46 # This probably means we need to directly invoke the interpreter. 47 # But first find clang-format-diff.py 48 if [ -f "$REPO_ROOT/clang-format-diff.py" ]; then 49 CFD_PATH="$REPO_ROOT/clang-format-diff.py" 50 elif which clang-format-diff.py &> /dev/null; then 51 CFD_PATH="$(which clang-format-diff.py)" 52 else 53 echo "You didn't have clang-format-diff.py and/or clang-format available in your computer!" 54 echo "You can download clang-format-diff.py by running: " 55 echo " curl --location https://raw.githubusercontent.com/llvm/llvm-project/main/clang/tools/clang-format/clang-format-diff.py -o ${REPO_ROOT}/clang-format-diff.py" 56 echo "You should make sure the downloaded script is not compromised." 57 echo "You can download clang-format by running:" 58 echo " brew install clang-format" 59 echo " Or" 60 echo " apt install clang-format" 61 echo " This might work too:" 62 echo " yum install git-clang-format" 63 echo "Then make sure clang-format is available and executable from \$PATH:" 64 echo " clang-format --version" 65 exit 128 66 fi 67 # Check argparse pre-req on interpreter, or it will fail 68 if echo import argparse | ${PYTHON:-python3}; then 69 true # Good 70 else 71 echo "To run clang-format-diff.py, we'll need the library "argparse" to be" 72 echo "installed. You can try either of the follow ways to install it:" 73 echo " 1. Manually download argparse: https://pypi.python.org/pypi/argparse" 74 echo " 2. easy_install argparse (if you have easy_install)" 75 echo " 3. pip install argparse (if you have pip)" 76 exit 129 77 fi 78 # Unfortunately, some machines have a Python2 clang-format-diff.py 79 # installed but only a Python3 interpreter installed. Unfortunately, 80 # automatic 2to3 migration is insufficient, so suggest downloading latest. 81 if grep -q "print '" "$CFD_PATH" && \ 82 ${PYTHON:-python3} --version | grep -q 'ython 3'; then 83 echo "You have clang-format-diff.py for Python 2 but are using a Python 3" 84 echo "interpreter (${PYTHON:-python3})." 85 echo "You can download clang-format-diff.py for Python 3 by running: " 86 echo " curl --location https://raw.githubusercontent.com/llvm/llvm-project/main/clang/tools/clang-format/clang-format-diff.py -o ${REPO_ROOT}/clang-format-diff.py" 87 echo "You should make sure the downloaded script is not compromised." 88 exit 130 89 fi 90 CLANG_FORMAT_DIFF="${PYTHON:-python3} $CFD_PATH" 91 # This had better work after all those checks 92 if $CLANG_FORMAT_DIFF --help >/dev/null < /dev/null; then 93 true #Good 94 else 95 exit 128 96 fi 97 fi 98fi 99 100# TODO(kailiu) following work is not complete since we still need to figure 101# out how to add the modified files done pre-commit hook to git's commit index. 102# 103# Check if this script has already been added to pre-commit hook. 104# Will suggest user to add this script to pre-commit hook if their pre-commit 105# is empty. 106# PRE_COMMIT_SCRIPT_PATH="`git rev-parse --show-toplevel`/.git/hooks/pre-commit" 107# if ! ls $PRE_COMMIT_SCRIPT_PATH &> /dev/null 108# then 109# echo "Would you like to add this script to pre-commit hook, which will do " 110# echo -n "the format check for all the affected lines before you check in (y/n):" 111# read add_to_hook 112# if [ "$add_to_hook" == "y" ] 113# then 114# ln -s `git rev-parse --show-toplevel`/build_tools/format-diff.sh $PRE_COMMIT_SCRIPT_PATH 115# fi 116# fi 117set -e 118 119uncommitted_code=`git diff HEAD` 120 121# If there's no uncommitted changes, we assume user are doing post-commit 122# format check, in which case we'll try to check the modified lines vs. the 123# facebook/rocksdb.git master branch. Otherwise, we'll check format of the 124# uncommitted code only. 125if [ -z "$uncommitted_code" ] 126then 127 # Attempt to get name of facebook/rocksdb.git remote. 128 [ "$FORMAT_REMOTE" ] || FORMAT_REMOTE="$(git remote -v | grep 'facebook/rocksdb.git' | head -n 1 | cut -f 1)" 129 # Fall back on 'origin' if that fails 130 [ "$FORMAT_REMOTE" ] || FORMAT_REMOTE=origin 131 # Use master branch from that remote 132 [ "$FORMAT_UPSTREAM" ] || FORMAT_UPSTREAM="$FORMAT_REMOTE/master" 133 # Get the common ancestor with that remote branch. Everything after that 134 # common ancestor would be considered the contents of a pull request, so 135 # should be relevant for formatting fixes. 136 FORMAT_UPSTREAM_MERGE_BASE="$(git merge-base "$FORMAT_UPSTREAM" HEAD)" 137 # Get the differences 138 diffs=$(git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -p 1) 139 echo "Checking format of changes not yet in $FORMAT_UPSTREAM..." 140else 141 # Check the format of uncommitted lines, 142 diffs=$(git diff -U0 HEAD | $CLANG_FORMAT_DIFF -p 1) 143 echo "Checking format of uncommitted changes..." 144fi 145 146if [ -z "$diffs" ] 147then 148 echo "Nothing needs to be reformatted!" 149 exit 0 150elif [ $CHECK_ONLY ] 151then 152 echo "Your change has unformatted code. Please run make format!" 153 if [ $VERBOSE_CHECK ]; then 154 clang-format --version 155 echo "$diffs" 156 fi 157 exit 1 158fi 159 160# Highlight the insertion/deletion from the clang-format-diff.py's output 161COLOR_END="\033[0m" 162COLOR_RED="\033[0;31m" 163COLOR_GREEN="\033[0;32m" 164 165echo -e "Detect lines that doesn't follow the format rules:\r" 166# Add the color to the diff. lines added will be green; lines removed will be red. 167echo "$diffs" | 168 sed -e "s/\(^-.*$\)/`echo -e \"$COLOR_RED\1$COLOR_END\"`/" | 169 sed -e "s/\(^+.*$\)/`echo -e \"$COLOR_GREEN\1$COLOR_END\"`/" 170 171if [[ "$OPT" == *"-DTRAVIS"* ]] 172then 173 exit 1 174fi 175 176echo -e "Would you like to fix the format automatically (y/n): \c" 177 178# Make sure under any mode, we can read user input. 179exec < /dev/tty 180read to_fix 181 182if [ "$to_fix" != "y" ] 183then 184 exit 1 185fi 186 187# Do in-place format adjustment. 188if [ -z "$uncommitted_code" ] 189then 190 git diff -U0 "$FORMAT_UPSTREAM_MERGE_BASE" | $CLANG_FORMAT_DIFF -i -p 1 191else 192 git diff -U0 HEAD | $CLANG_FORMAT_DIFF -i -p 1 193fi 194echo "Files reformatted!" 195 196# Amend to last commit if user do the post-commit format check 197if [ -z "$uncommitted_code" ]; then 198 echo -e "Would you like to amend the changes to last commit (`git log HEAD --oneline | head -1`)? (y/n): \c" 199 read to_amend 200 201 if [ "$to_amend" == "y" ] 202 then 203 git commit -a --amend --reuse-message HEAD 204 echo "Amended to last commit" 205 fi 206fi 207