Guile, Autotools and the std-options option

In a prior post, I wrote about getting Autotools working with Guile. There's one more little nit I had to address: std-options. The std-options option to Automake makes "…the installcheck rule check that installed scripts and programs support the –help and –version options." For me, this came up when making distcheck for my Guile projects: after installing my script to a temporary location, it would attempt to run it with both the --version & --help options. If my program needed some Scheme modules, it would fail to load them, as distcheck installed them in some temporary location not on GUILE_LOAD_PATH. Regardless, Guile would attempt to compile the new script, printing a message to that effect on stderr, which the installcheck target checks.

Opening up the makefile, we see:

installcheck: installcheck-am
...
installcheck-am: installcheck-dist_binSCRIPTS installcheck-local
...
installcheck-dist_binSCRIPTS: $(dist_bin_SCRIPTS)
	bad=0; pid=$$$$; list="$(dist_bin_SCRIPTS)"; for p in $$list; do \
	  case ' $(AM_INSTALLCHECK_STD_OPTIONS_EXEMPT) ' in \
	   *" $$p "* | *" $(srcdir)/$$p "*) continue;; \
	  esac; \
	  f=`echo "$$p" | sed 's,^.*/,,;$(transform)'`; \
	  for opt in --help --version; do \
	    if "$(DESTDIR)$(bindir)/$$f" $$opt >c$${pid}_.out \
	         2>c$${pid}_.err </dev/null \
		 && test -n "`cat c$${pid}_.out`" \
		 && test -z "`cat c$${pid}_.err`"; then :; \
	    else echo "$$f does not support $$opt" 1>&2; bad=1; fi; \
	  done; \
	done; rm -f c$${pid}_.???; exit $$bad

Very briefly, what this rule does is iterate over every executable in $(dist_bin_SCRIPTS) & run it with first the --help option, then the --version option. For each invocation, it captures stdout, stderr, and the exit status. If nothing is produced on stdout, or if anything is produced on stderr, or the exit status is not zero, we exit with status 1. In other words, Automake is just checking that your scripts support a few standard options, and in a very basic manner.

So I was stuck in a situation where my package would install properly "in the wild", but I couldn't get make distcheck to pass. I found my solution in the rule above: look at the case statement before the invocation– we're checking a variable AM_INSTALLCHECK_STD_OPTIONS_EXEMPT and if a program is listed there, we skip this check: "In a few situations, programs (or scripts) have to be exempted from this test. For instance, false (from GNU coreutils) is never successful, even for –help or –version. You can list such programs in the variable AM_INSTALLCHECK_STD_OPTIONS_EXEMPT."

So adding AM_INSTALLCHECK_STD_OPTIONS_EXEMPT = $(SOURCES) to my Makefile.am would get distcheck working again… but still. That's actually a handy check. I didn't really want to eliminate it so much as customize it.

I can't remember how I found this, but the Automake manual explains "…various useful targets have a ‘-local’ version you can specify in your Makefile.am. Automake will supplement the standard target with these user-supplied targets." installcheck is one of them– all I had to do was add the following to my Makefile.am and I was back in business:

# Here is where we write our own `installcheck' rule, replicating the default one we disabled above,
# this time setting GUILE_LOAD_COMPILED_PATH so that the installed script will run whereever it
# was installed. Note that because we do _not_ specify GUILE_LOAD_PATH, if the script(s) depend
# on Guile modules in this package, this test will fail if the modules were not compiled & installed
# properly.
installcheck-local:
	bad=0; pid=$$$$; list="$(dist_bin_SCRIPTS)"; for p in $$list; do \
	  f=`echo "$$p" | sed 's,^.*/,,;$(transform)'`; \
	  for opt in --help --version; do \
	    if GUILE_LOAD_COMPILED_PATH="$(ccachedir):$(binccachedir)" "$(DESTDIR)$(bindir)/$$f" $$opt >c$${pid}_.out \
	         2>c$${pid}_.err </dev/null \
		 && test -n "`cat c$${pid}_.out`" \
		 && test -z "`cat c$${pid}_.err`"; then :; \
	    else echo "$$f does not support $$opt; $$?|`cat c$${pid}_.out`|`cat c$${pid}_.err`" 1>&2; bad=1; fi; \
	  done; \
	done; rm -f c$${pid}_.???; exit $$bad

For a description of $(ccachedir) and $(binccachedir), see my prior post. All quotes are from the Automake manual.

04/21/20 07:29