(this is a few days old -- I started it before last weekend. So take all present tense to be past tense)
Learned some wisdom today. It was painful, so I'm going to share in the hope that others may save some time...
On the eternal quest to have "proper" Makefiles, we had quite an elaborate setup for dependencies in LAM/MPI (the automake stuff for generating dependencies is broken for non-GNU make). The only problem was, it didn't work for VPATH builds. We were somehow under the mistaken impression that you didn't need make depend in VPATH builds.
Sidenote: For those of you unfamiliar with VPATH builds, it's a slight variation on the GNU standard "
./configure ; make all install" Scheme of Doing Things. It allows you to use one source code tree to build multiple binary trees. i.e., you download a random tarball, expand it to its source tree, and then run "./configure ; make all install" multiple times simultaneously. What's the benefit? For building on multiple architectures, and/or with differentconfigureoptions, of course! If you think about it, this is a really handy mechanism.It works like this (I slightly lied above): you expand the tarball, and make a new directory to build in. And then run
configure(andmake) from that new directory. For example:
unix% gunzip -c foo-1.0.2.tar.gz | tar xf -
# ...makes foo-1.0.2/ directory...
unix% mkdir build
unix% cd build
unix% mkdir sparc-sun-solaris2.7
unix% cd sparc-sun-solaris2.7
unix% ../../foo-1.0.2/configure \
--prefix=/yadda/yadda/yadda/sparc-sun-solaris2.7
# ...much output...
unix% gmake all install(The final "
gmake" is necessary because Sun's nativemakeisn't VPATH enabled)Hence, you can have multiple of these puppies running simultaneously, all from the same code tree. This is really handy in development, too, when you need to test on multiple architectures simultaneously.
But now I see the error of my ways (it took developing on Solaris and Linux simultaneously with the same code base to show me this piece of wisdom). Hence, I set about to make our depend target work properly for VPATH and non-VPATH builds. Easier said than done.
Although I already knew this, I have finally and firmly decided that make's rules for syntax (particularly quoting) SUCK.
We use the GNU tools automake and libtool to build LAM/MPI (the use of libtool doesn't actually matter here, I just wanted to use it to mention our sponsors -- buy GE products today). Now previous journal entries have shown how automake can be your friend, but automake can also be your enemy (very similar to power tools, in this respect). This journal entry has nothing to do with automake (buy GE appliances).
In our automake setup, we include a top-level Makefile.depend file that has our "depend" target. It was fairly lengthy and involved, and it applied to the whole tree, so this made sense.
For an hour or two, I tried to make it do VPATH stuff properly. This involved the following:
- Getting the source file list
- Running
makedependon all of the source files
Sounds pretty simple, eh? Not so, gentle reader, not so. Here's why:
- First off, GNU
makesucks. I don't know if this is a documented "feature" or not, but it certainly makes no sense to me. So when you have a list of source files (e.g, "BLAH = foo.c bar.c baz.c"), GNUmakehappily prefixes each of them with the VPATH for you.Whoo hoo! This saves a lot of trouble of doing it manually. After all, none of the source code files are actually in this directory -- we have to add some kind of prefix to get to each of them.
However -- closer examination reveals
gmake's suckage. The last file in the list does not get the VPATH prefix applied! Why? I have no idea. But it pretty much fucks up the whole scheme -- it's pretty useless to get all but the last one.It's not ok if you only get five chicken McNuggets when you order the six-piece combo at the drive through. Heck, no. You get all six or its throwdown time.
As such, I had to write code to a) strip off the VPATH prefix from each entry (if it was there), and then b) add it back on to every entry. Not that this was extraordinarily difficult (but escaping the
sedexpressions in theMakefilewas a bitch...), but I shouldn't have had to do this. - With the re-VPATH-prefixed list of source files, you can run
makedepend. But oops, it barfs. It seems that it can't find the filelam_config.h. Arrgh -- that's the one thatconfiguregenerated viaautoheader. It seems thatautomakeisn't smart enough to add-I$(top_builddir)/share/includetoCFLAGS--
it adds-I$(srcdir)/share/includeinstead. What the hell is the point of that?(translation:
automakeis adding a-Ifor the source tree, not the build tree. But the config.his always put in the build tree -- not the source tree. So I'm not quite sure what the logic is here)So we have to manually add the
-Ifor the build tree. Not nice -- we shouldn't have to do this -- but very easy to do, so move on. - Whoo hoo! It seemed to work! Checking the generated
Makefile... #@$%@#$%@#%@#$%!!!!!!!All the dependency entries are for "
VPATH/foo.o", and "VPATH/bar.o", etc. instead of "foo.o" and "bar.o". That is, we're buildingfoo.o, not../../foo-1.0.3/src/foo.o. Hence, theMakefilehas to show the right dependency.CRAP.
So we have to add some more
sedmojo to post-parse the Makefile and strip out the VPATH prefixes from the generated dependencies. - Ok, run again. Seems to work this time. Let's try it on the whole source tree...
Barf-o-rama. One of the source directories in LAM has almost 250 source files in it. Adding "
../../lam-6.3.3b44/share/mpi" to every entry in the list quickly overflowed the shell's buffer for a single variable. Hence, it just dropped all the additional filenames.So I had to add a loop around the file list to only process about 20-25 at a time. <sigh> This really became painful at some point; I hurt.
Trying once more... #@$%@#$%@#$%@#!!!!!
Since we're running
makedependmultiple times, it only saves the output of the last run in the generatedMakefile. Hence, it saves the dependencies of the last 20 or so files; all the previous dependencies are snipped each timemakedependruns.Luckily,
makedependhas a-foption to specify where to send the output, so we can save it in a temp file and tack on successive results to the end of theMakefile.
- Try again.... <sigh> Still no love.
Now it's not ditching the previous results at all. Since
makedependisn't running on the mainMakefile, it doesn't snip the previous dependencies. Hence, we have to do it ourselves. Redirect some input toedto snip out all lines after "# DO NOT DELETE" (seems pretty ironic, doesn't it?) and catenate the new results on after that. - Finally... it works.
That whole process actually took quite a while -- adding additional quoting for make (especially in the sed expressions) made it arbitrarily difficult. So somewhere near the end, I said fuck it, and moved the whole thing off to a bourne shell script. It actually became much easier at that point -- I should have done that much earlier. The depend target actually became pretty small at that point; it just calls that script with a small number of arguments followed by the list of files (also as command line arguments to prevent single-shell-variable-overflows).
The moral of the story: it works now. It works for VPATH, it works for non-VPATH. If you want the script, LAM's anonymous CVS access
-- it's config/run_makedepend. The depend target itself is in the top-level directory, a file named Makefile.depend.
Save the planet: reuse code. Feel free to steal/improve this depend target. Your country depends on it.
It's all about the subliminal.