Reputation: 60056
In an autoools-based build, I'd like to replace a version-controlled C file with a generated C file.
This dummy, hello world example sort-of works:
#!/bin/sh -ex
cat > configure.ac <<EOF
AC_INIT([hello], [0.01])
AC_PREREQ([2.68])
AC_CONFIG_SRCDIR([hello.c])
AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([1.11])
AC_CONFIG_FILES([Makefile])
AC_PROG_CC
AC_OUTPUT
EOF
cat > autogen.sh <<EOF
touch NEWS README AUTHORS ChangeLog COPYING
autoreconf -i
EOF
chmod +x autogen.sh
printf "
bin_PROGRAMS = hello
hello_SOURCES = hello.c
hello.c: generate
\t./generate
" > Makefile.am
cat > hello.c <<EOF
#include <stdio.h>
int main()
{
puts("hello world");
return 0;
}
EOF
cat > generate <<EOF0
#!/bin/sh
cat > hello.c <<EOF
#include <stdio.h>
int main()
{
puts("HELLO WORLD");
return 0;
}
EOF
EOF0
chmod +x generate
./autogen.sh
./configure
make -j$(nproc)
except it prevents me from doing an out-of-tree build such as:
mkdir B
cd B
../configure
make
which I can normally do in an autotools-based package. How can I generated the C file so that out-of-tree builds continue to work?
Upvotes: 0
Views: 182
Reputation: 180048
It is important to remember that make
doesn't really know much about directories; for the most part, everything is a string to it. That's not always so obvious, but it tends to really bite you when you are trying to write a build system that can handle out-of-source builds.
You provide a build rule wherein hello.c
depends on your generate
script, which is fine in principle. Make
will even handle the dependency processing correctly in an out-of-source build, finding the generate
script in the source directory. But the recipe in the build rule explicitly runs ./generate
, which does not exist when you're performing an out-of-source build. Furthermore, you have a disconnect between the dependency and the build rule: 'generate' is not the same thing as './generate'.
There are two main alternatives:
In your build rules, rely only on automatic make
variables (e.g. $<
) to refer to dependencies. When make
is performing an out-of-source build, it initializes automatic variables with viable paths.
Refer to files in the source directory via explicit paths.
To use option (1) by itself, you would need to work around the problem of how to run the generate
script when it is (presumably) not in the path. You can do that by running it indirectly, via the shell. The make rule you would want in that case would be
hello.c: generate
$(SHELL) $<
That should work for either in-source or out-of-source building. The $(SHELL)
make variable should be provided by the Autotools.
To use option (2), on the other hand, you would probably rely on $(srcdir)
or $(top_srcdir)
:
# Note that the dependency name will always contain a '/':
hello.c: $(srcdir)/generate
$<
(You can also name the generate script, with path, in the build recipe, but it's usually best to avoid repeating yourself.) The $(srcdir)
and $(top_srcdir)
variables are also provided by the Autotools. The former refers to the directory in the source tree that corresponds to the build-tree directory we are currently building; the latter refers to the source-tree directory containing the configure
script.
Of those two, I'd rate (1) a bit more in the spirit of out-of-source building, but there isn't really that much difference.
Upvotes: 2