Guile & Autoconf
I use Autotools to organize my projects (yes, I know; but to paraphrase Churchill it's the worst solution, except for all the others). While working on a Guile Scheme project recently, I became frustrated trying to figure out how to install my compiled Scheme files (i.e. .go
files).
This is the best discussion I've found, but it concerns the details of authoring an Automake file that will install compiled Scheme files. Their install destination is simply assumed to be ~/.cache/guile/ccache
, but that is no longer the case, at least on my system. Of course, given that this discussion took place in 2010 and pertains to Guile 1.9, that's not surprising.
This post sets it to $(libdir)/guile/$(GUILE_EFFECTIVE_VERSION)/site-ccache
, which is not a bad guess, but not always correct. For instance, on one of my systems, the site-ccache is /usr/lib/x86_64-linux-gnu/guile/2.2/cache
.
The Autoconf macro GUILE_SITE_DIRS
(see here) will set the variable GUILE_SITE_CCACHE
based on pkgconfig
, which seems correct when installing to the default location, but that's not always what you want. Suppose someone configures your package with a prefix of, say, $HOME
? They would then reasonably expect to be able to install without escalating privileges, and will be quite surprised when the installation fails for your .go
files (unless they run the install as root
).
Worse, when making the distcheck
target, Automake will try to install the package into a temporary location as the maintainer running make
. If you have Makefile rules trying to install .go
files to GUILE_SITE_CCACHE
, which will typically be under /usr/lib
, or /usr/local/lib
, make distcheck
will fail.
Since I can't know a priori what the user will want to do, I fell back to having them tell me. Autoconf doesn't permit maintainers to add new command-line options (other than enabling or disabling optional packages), but it does allow us to add environment variables which will be declared precious and described in the output of configure --help
(see here for more details).
I introduced such an environment variable (MY_PKG_GUILE_SITE
, in this example). The rules for how it will be used are as follows:
- if, at configure time,
${MY_PKG_GUILE_SITE}
is non-empty, use that as the install location for compiled Scheme files - otherwise, if
${prefix}
is "NONE" (i.e. the user has not selected a particular prefix)- if
GUILE_SITE_CCACHE
is not empty, useGUILE_SITE_CCACHE
- else, use
${ac_default_prefix}/lib/guile/${GUILE_EFFECTIVE_VERSION}/site-ccache
- if
- else, use
${prefix}/lib/guile/${GUILE_EFFECTIVE_VERSION}/site-ccache
This seems to handle all the cases I can think of:
- the typical user who does
./configure && make && sudo make install
will get the expected behavior make distcheck
will work- someone who wants an un-privileged install can say
./configure --prefix=$HOME && make && make install
and have it work; if they don't like my guess as to where to put compiled files, they can override it.
Here's a snippet from configure.ac
that demonstrates the technique:
AC_ARG_VAR([MY_PKG_GUILE_SITE], [The directory under which compiled Scheme files shall be installed. Unset or empty means install into $(libdir)/guile/$GUILE_EFFECTIVE_VERSION/site-ccache.]) if test -n "${MY_PKG_GUILE_SITE}"; then _MY_PKG_GUILE_SITE="${MY_PKG_GUILE_SITE}" elif test "${prefix}" == "NONE"; then if test -n "${GUILE_SITE_CCACHE}"; then _MY_PKG_GUILE_SITE="${GUILE_SITE_CCACHE}" else _MY_PKG_GUILE_SITE="${ac_default_prefix}/lib/guile/${GUILE_EFFECTIVE_VERSION}/site-ccache" fi else _MY_PKG_GUILE_SITE="${prefix}/lib/guile/${GUILE_EFFECTIVE_VERSION}/site-ccache" fi AC_MSG_NOTICE([Compiled Scheme files will be installed under ${_MY_PKG_GUILE_SITE}]) AC_SUBST([_MY_PKG_GUILE_SITE])
I wind up substituting a variable, _MY_PKG_GUILE_SITE
with the final location to which .go
files shall be installed– this can be output to a file via AC_OUTPUT
, but the variable will be available to make
, as well. In my Makefile.am
, I can follow the approach laid out in the first reference above (a guile-devel thread) and set ccachedir
to $(_MY_PKG_GUILE_SITE)
if I'm going to install my .go
files directly into the Guile site directory, or to $(_MY_PKG_GUILE_SITE/my-package-directory
if I want to setup a sub-directory for my package.
But wait– all of the arguments above apply to installing the Scheme source for my modules, as well: I can get the system Guile site directory (/usr/local/share/guile/site
, e.g.) in the variable ${GUILE_SITE}
from the same GUILE_SITE_DIRS
macro above, but that, like GUILE_SITE_CCACHE
, doesn't respect the prefix argument to configure
, so once again neither make distcheck
nor un-privileged installations would work. I wound up taking the same approach– introduce an environment variable to my configure script that the user can use to explicitly say where they want the module source installed.
Here's a complete solution, assuming I want to install my module under directory my-package
, and that my package consists of two files: foo.scm
and bar.scm
.
configure.ac
:
GUILE_SITE_DIR dnl This logic decides where Scheme modules go. The GUILE_SITE_DIR dnl call, above, retrieves the location as recorded by `pkgconfig', dnl but that's not appropriate for an installation to a non-standard dnl place via the --prefix option. The rules are as follows: dnl 1. if ${MY_PKG_GUILE_SITE} is non-empty, use that dnl 2. if ${prefix} is "NONE" dnl - if GUILE_SITE is not empty, use GUILE_SITE dnl - use ${ac_default_prefix}/share/guile/site dnl 3. use ${prefix}/share/guile/site AC_ARG_VAR([MY_PKG_GUILE_SITE], [The directory under which Scheme modules shall be installed. Unset or empty means install into ${datadir}/guile/site.]) if test -n "${MY_PKG_GUILE_SITE}"; then _MY_PKG_GUILE_SITE="${MY_PKG_GUILE_SITE}" elif test "${prefix}" == "NONE"; then if test -n "${GUILE_SITE}"; then _MY_PKG_GUILE_SITE="${GUILE_SITE}" else _MY_PKG_GUILE_SITE="${ac_default_prefix}/share/guile/site" fi else _MY_PKG_GUILE_SITE="${prefix}/share/guile/site" fi AC_MSG_NOTICE([Scheme modules will be installed under ${_MY_PKG_GUILE_SITE}]) AC_SUBST([_MY_PKG_GUILE_SITE]) dnl This logic decides where compiled Schmee files (`.go' files) shall be installed. dnl The rules are as follows: dnl 1. if ${MY_PKG_SITE_CCACHE} is non-empty, use that dnl 2. if ${prefix} is "NONE" dnl - if GUILE_SITE_CCACHE is not empty, use GUILE_SITE_CCACHE dnl - use ${ac_default_prefix}/lib/guile/${GUILE_EFFECTIVE_VERSION}/site-ccache dnl 3. use ${prefix}/lib/guile/${GUILE_EFFECTIVE_VERSION}/site-ccache AC_ARG_VAR([MY_PKG_GUILE_CCACHE], [The directory under which compiled Scheme files shall be installed. Unset or empty means install into $(libdir)/guile/$GUILE_EFFECTIVE_VERSION/site-ccache.]) if test -n "${MY_PKG_GUILE_CCACHE}"; then _MY_PKG_GUILE_CCACHE="${MY_PKG_GUILE_CCACHE}" elif test "${prefix}" == "NONE"; then if test -n "${GUILE_SITE_CCACHE}"; then _MY_PKG_GUILE_CCACHE="${GUILE_SITE_CCACHE}" else _MY_PKG_GUILE_CCACHE="${ac_default_prefix}/lib/guile/${GUILE_EFFECTIVE_VERSION}/site-ccache" fi else _MY_PKG_GUILE_CCACHE="${prefix}/lib/guile/${GUILE_EFFECTIVE_VERSION}/site-ccache" fi AC_MSG_NOTICE([Compiled Scheme files will be installed under ${_MY_PKG_GUILE_CCACHE}]) AC_SUBST([_MY_PKG_GUILE_CCACHE])
my-package/Makefile.am
:
moddir = $(_MY_PKG_GUILE_SITE)/my-package ccachedir = $(_MY_PKG_GUILE_CCACHE)/my-package SOURCES = foo.scm bar.scm EXTRA_DIST = $(SOURCES) GOBJECTS = $(SOURCES:%.scm=%.go) # Per the Automake manual "If make built it, and it is commonly something that one would want to # rebuild (for instance, a .o file), then mostlyclean should delete it."-- that's why I put the # `.go' objects here. MOSTLYCLEANFILES = $(GOBJECTS) mod_DATA = $(SOURCES) ccache_DATA = $(GOBJECTS) # Here https://lists.gnu.org/archive/html/guile-devel/2010-07/msg00125.html is the famous e-mail # (I've seen this one e-mail message cited in multiple Guile projects at which I've looked) explaining # how to install the script first, and then their compiled `.go' counterparts, so that the compiled # versions will have a later timestamp. guile_install_go_files = install-ccacheDATA $(guile_install_go_files): install-modDATA GUILD_WARNINGS = -Wunbound-variable -Warity-mismatch -Wformat SUFFIXES = .scm .go .scm.go: $(GUILD) compile -L $(srcdir)/.. $(GUILD_WARNINGS) -o "$@" "$<"
I've been using this in my Guile Scheme projects for some time now, and it has worked well for my needs. Still, it feels inelegant, not in keeping with the intent of GNU Autotools. Until Automake adds native support for Scheme projects, we will have to author our own rules. But using environment variables to control install destinations seems wrong, somehow. For instance, even with this solution, if GUILE_SITE_CCACHE
on a given system is /usr/local/lib/x86_64-linux-gnu/guile/2.2/cache
, and the user says configure --prefix=/usr/local
, the compiled Scheme files will wind up in the wrong location (with my scheme, they would end up in /usr/local/lib/guile/$(GUILE_EFFECTIVE_VERSION)/site-ccache
). Yes, if the user is careful, runs sudo make -n install
and notices, they can override that with my environment variable, but still. Anyone else dealt with this?
03/19/20 11:30