2010-04-30

Run checkpatch automatically on git commit

This is a script I wrote that hooks into git-commit, and automatically runs checkpatch and “git diff –check” on a commit. If any of these fail, the commit is rejected. (In case you are not already using it, checkpatch looks for common coding mistakes, and it facilitates better kernel code.)

In addition, the script sets file permissions for common files (c, h, makefile, readme) to 0644, and executables (directories, shell scripts) to 0755. This is a common issue when editing files in Windows.

To install, save the script in your kernel GIT repository/ies under the $GIT_DIR/hooks directory (usually .git/hooks) as .git/hooks/pre-commit.

If you want to bypass the check (e.g. if the failure is intended), use the -n flag for git-commit to ignore the checks. Just be sure that you still resolve any failures that were correct, and there really wasn’t a better proper way to write your code without checkpatch errors. Now there is a whole set of discussion about the 80 character limit that sometime forces less understandable code (you can read about the pros/cons and find a patch to checkpatch to make this optional) BTW, Linus prefers in many cases to ignore the 80-char-limit warnings.

But wait! You can do even more. Since you are already modifying files, why not run checkpatch on the whole file (and not just on the lines you are changing)? To see if there are any already existing checkpatch errors in the files that you modified, set up these two bash aliases that will run checkpatch on the whole files that are about to be committed (just run ‘chkp’ - NOTE: this will not work in an empty repository.):

alias gitd='git rev-parse --show-cdup'
alias chkp='`gitd`scripts/checkpatch.pl -f `git diff --name-only HEAD | awk -v i=$(gitd) "{print i\\$0}"`'
Let me know if you have any questions/suggestions.
#!/bin/bash
#
# Run checkpatch.pl and fix permissions on what is about to be
# committed.
#
# Written by Lajos Molnar  based on the sample git
# pre-commit script.

if git-rev-parse --verify HEAD >/dev/null 2>&1
then
  against=HEAD
else
  # Initial commit: diff against an empty tree object
  against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

# Change access modes on committed files - do this first so
# we fix permissions even if there are errors
for i in `git diff-index --cached --name-only $against`
do
  name=${i##*/}
  # set permissions for common kernel files
  if echo $name \
  | grep -Eq "^(README|Kconfig|Kbuild|Makefile|HOWTO|TODO|"\
"\.gitignore|.*\.(c|h|cpp|txt|S|xml|mk))$"
  then
    chmod 0644 $i
    git add $i
  # directories and scripts are executable
  elif [ -d $i ] \
  || (echo $name | grep -Eq "^.*\.(sh|pl|py)$") 
  || (head -1 $i | grep -Eq "^#!")
  then
    chmod 0755 $i
    git add $i
  fi
done

# run simple diff checks
git diff-index --cached $against --check -- || exit $?

# run checkpatch if there is actual code difference
if git diff --cached $against | grep -Eq "^---"
then
  (git diff --cached $against \
  | $GIT_DIR/../scripts/checkpatch.pl --no-signoff -) || exit $?
fi

exit 0

2010-03-22

#1 C Macro Pet Peeve: use do while 0

I cannot fathom why I still see compound macros like:
#define TRACE(fmt, ...) { \
printf(fmt, ##__VA_ARGS__); fflush(stdout); \
}

Please, use do while 0:
#define TRACE(fmt, ...) do { \
printf(fmt, ##__VA_ARGS__); fflush(stdout); \
} while (0)

Don't worry. Your (any remotely recent) compiler will not generate any assembly code for the "do while 0". For an explanation, just search do while 0. The basic idea is that you want these kind of macros to behave like normal C void methods. However, if you use the original macro in an expression such as "if (expr) TRACE("yes"); else TRACE("no");", the semicolons after the expansion will cause syntax errors.

For more options to use instead of do while 0, see G_STMT_START/END macros in glib.h, depending on what your compiler allows:
#define TRACE(fmt, ...) if(0) { \
printf(fmt, ##__VA_ARGS__); fflush(stdout); \
} else (void)0

#define TRACE(fmt, ...) (void) ({ \
printf(fmt, ##__VA_ARGS__); fflush(stdout); \
})

C Macro School

C is language I use most commonly. One of the least developed features of C (and coincidentally my favorite feature) is its macro preprocessor. Luckily, during the past decade GCC has implemented many improvements that ease the creation of very powerful macros.

Over the course of my projects, I very often work with someone else or on someone else's code, and I am surprised that even after 15 years, I keep seeing the same mistakes. So I am going to list my list of "My 10 Greatest Pet Peeves with C Macros". Well there may not be 10, but there are many. So let's begin...

Hello

Hi, welcome to my new blog on all things software. I am a software engineer. My friends tease me that I only attended an Institute. I am here to assert myself. Keep on reading...