Version detection versus feature detection

My friend plomlompom patched my implementation of redo to work on OS X. Here is the patch.

I did not merge the patch. The reason is that it introduces a regression – redo no longer working properly with BusyBox. Fundamentally, this happens because the code uses version detection: It checks the version string of another program and infers something about the other program's capabilities. The offending part of the code follows, with the important parts highlighted:

alias stat_cY='stat -c%Y'
test=`stat --version 2>&1 | grep GNU | wc -l`
if [ 1 -gt $test ]
then
     alias stat_cY='stat -f%Y'
fi

The above code means: If the stat program installed on the user's system produces at least one line that has the string GNU in it, assume it recognizes the option -c%Y; if the stat program is unable to output such a line, assume it recognizes the option -f%Y.

This code is meant to solve the question which command line option the implementation of stat installed on the user's system recognizes. For reference, the GNU implementation of stat only recognizes the option -c%Y, while the OS X implementation of stat only recognizes the option -f%Y.

Obviously, the code relies on faulty assumptions: It is trivial to write a stat program that outputs the string GNU many times but does not recognize any command line option, it is also possible to write a program that does recognize the command line option -c%Y, but does not output the string GNU and it is possible to write a program that does not output anything and does not recognize any command line option.

One program that does not output the string GNU but does recognize the option -c%Y is the BusyBox implementation of stat (which, ironically, complains about an unrecognized option '--version' when called like in the above code). While one could extend the above code to look for the string BusyBox in the output of stat, this would not suffice: The BusyBox implementation of stat installed on my phone does not recognize the option -c%Y.

The solution to all of the above problems is not doing version detection.

One should always do feature detection instead, i.e. check if a given feature actually works. Feature detection has the upside of being future-proof (in case the feature in question is deprecated) and catches programs that lie about their feature set. My implementation of redo uses feature detection to work around the stat differences:

stat -c%Y "$0" >/dev/null 2>&1 || alias stat=false

The above code means: If the stat program installed on the user's system exits non-zero if given the option -c%Y, alias stat to false. Using false instead of stat results in redo always assuming that a file is out of date. My redo implementation therefore rebuilds everything on systems where stat does not understand the command line option -c%Y.