Multiple makefiles

From LSDevLinux
Revision as of 23:02, 23 November 2010 by Spammer (talk | contribs)

Jump to: navigation, search

.PHONY method

There are times when it is desirable to use more than one makefile for a project. For example, the test code for VwGraphics depends on the VwGraphics library, which has its own makefile.

The following lines in the test makefile will make the library if it does not already exist:

    @cd $(@D); $(MAKE)$(@F)
  • The <code>@</code> before cd stops make from echoing the command
  • <code>$(@D)</code> is the directory (<code>../../lib</code>)
  • <code>$(@F)</code> is the file (<code>VwGraphics.o</code>)

This approach, however will only make the file if it does not exist. The file will not be rebuilt if it is out of date.

To use the smarts from the makefile in the lib directory, we can add <code>VwGraphics.o</code> to the dependencies of <code>.PHONY</code>. These dependencies will be rebuilt every time they are needed.

.PHONY: ../../lib/VwGraphics.o

However, with this approach, these files will be rebuilt even when they are not out of date.

Double colon Method

<p>I've found what I think is the best way to use build products from another Makefile ("remote dependencies"). You want two things:</p>

<ol> <li>To rebuild the current target if the remote dependency has changed</li> <li>To rebuild the remote dependency whenever this is needed</li> </ol>

<p>For example, suppose you are writing a test program for a library, <code>libFoo.a</code>, and that the test program is in a subdirectory of the directory where <code>libFoo</code> is built. Your test program's Makefile would contain the following dependency:</p>

<blockquote> <pre>TestFoo: TestFoo.o ../libFoo.a</pre> </blockquote>

<p>However, you want <code>../libFoo.a</code> to be remade if it doesn't exist. So you include a rule like this:</p>

<blockquote> <pre>../libFoo.a:

   $(MAKE) -C $(@D) $(@F)</pre>


<p>This says to run make in the appropriate directory (<code>$(@D)</code>, ..) to make the appropriate target (<code>$(@F)</code>, <code>libFoo.a</code>). Unfortunately, unless <code>libFoo.a</code> doesn't exist, it won't be remade, even if it is out of date, because the current Makefile doesn't know about <code>libFoo.a</code>'s dependencies.</p>

<p>The answer, I've found, is to use a double-colon rule for <code>../libFoo.a</code>. If there are no dependencies, the target's commands will always be run, due to the slightly different meaning of double-colon rules. However, things that depend on this target won't be remade unless this target is actually rebuilt (ie the file's modification time changes).</p>

<p>However, there's still a slight annoyance. If <code>libFoo.a</code> doesn't need to be remade, you will get the message "<code>make[1]: `libFoo.a' is up to date.</code>". To avoid this, it's necessary to strengthen the recursive make command:</p>

<blockquote> <pre>../libFoo.a::

   @$(MAKE) -C $(@D) $(@F) -q || \
    $(MAKE) -C $(@D) $(@F)</pre>


<p>This runs two recursive make commands. The first, with a <code>-q</code> option, simply tests whether the target needs to be remade. The second, coupled to the first with the shell's OR operator (<code>||</code>) only runs if the first 'fails'. In addition, the leading <code>@</code> is used to suppress echoing of the whole compound command.</p>

<p>For a really complex build, it would of course be expensive to run make twice in this way, but for most practical purposes the overhead isn't noticeable.</p>