For Programmers: Free Programming Magazines  


Home > Archive > Unix Programming > December 2006 > Strange behavior with Make dependencies...









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author Strange behavior with Make dependencies...
João Jerónimo

2006-12-18, 7:07 pm

This Makefile is having a strange behavior:
(I'll add comments about how I think it should work)

---------------------------------------------------

#Run this to update everything...
#Try to update the file floppy.img and, if it's not
#up-to-date, then do nothing!

all: floppy.img




#Run this to update floppy.img, but only if bootsect.MBR
#or bootldr.sys are not up to date...
#Before running, try to update bootsect.MBR and bootldr.sys

floppy.img: bootsect.MBR bootldr.sys
cat bootsect.MBR bootldr.sys > floppy.img




#Run this to update bootsect.MBR

bootsect.MBR:
make -C bootsect
#(this command will place an updated bootsect.MBR in
#the current directory if it's not already up-to-date)




#Run this to update bootldr.sys

bootldr.sys:
make -C bootldr
#(this command will place an updated bootldr.sys in
#the current directory if it's not already up-to-date)







#Run this to clean everything...

clean:
make -C bootsect clean
make -C bootldr clean
rm -f floppy.img

-------------------------------------------------------

So, "make -C bootsect" should be always executed... then, if this
command results in updating bootsect.MBR, the command "cat bootsect.MBR
bootldr.sys > floppy.img" should be ran...



When I do make in the directory where *this* makefile is, Make tells me:
make: Nothing to be done for `all'.
Whether I've updated some file inside any of the two subdirectories or
not... so, when I update some file, it's not propagated, which is wrong...
The makefile should always run "make -C bootsect" and "make -C bootldr",
but it never runs it, unless I'd run "make clean" before...

JJ
João Jerónimo

2006-12-18, 7:07 pm

João Jerónimo wrote:
> So, "make -C bootsect" should be always executed... then, if this
> command results in updating bootsect.MBR, the command "cat bootsect.MBR
> bootldr.sys > floppy.img" should be ran...
>
>
>
> When I do make in the directory where *this* makefile is, Make tells me:
> make: Nothing to be done for `all'.
> Whether I've updated some file inside any of the two subdirectories or
> not... so, when I update some file, it's not propagated, which is wrong...
> The makefile should always run "make -C bootsect" and "make -C bootldr",
> but it never runs it, unless I'd run "make clean" before...


I've just solved my problem by telling that bootsect was a dependency of
bootsect.MBR :

bootsect.MBR: bootsect

Then, Make will check if the timestamp of the bootsect directory is
later than the bootsect.MBR's and run the target if so...

But still it doesn't make sense, cause when a target has no
dependencies, it is always run... at least AFAIK...

JJ
toby

2006-12-18, 7:07 pm

Jo=E3o Jer=F3nimo wrote:
> Jo=E3o Jer=F3nimo wrote:
..=2E.[color=darkred]
>
> I've just solved my problem by telling that bootsect was a dependency of
> bootsect.MBR :
>
> bootsect.MBR: bootsect
>
> Then, Make will check if the timestamp of the bootsect directory is
> later than the bootsect.MBR's and run the target if so...


This is an unreliable workaround. (One reason is because the directory
timestamp does not reflect all changes to files inside it - it will
update when files are added or removed, but you can change contents of
a file without this being reflected in the parent dir's timestamp.)

>
> But still it doesn't make sense, cause when a target has no
> dependencies, it is always run... at least AFAIK...


NOT if it exists[1]. The build command is being skipped because make
sees that 'bootsect.MBR' already exists and is satisfied. Because the
rule has no pre-requisites, it has no way to know if it is out of date.
The correct fix to your problem is to add the correct prerequisite(s)
to your rule for bootsect.MBR - which are the input files used by the
rule's command(s). The same needs to be done for 'bootldr.sys' rule.

It is frequently a sign of a mistake in a Makefile when a real target
file (not a phony target like 'all', 'clean' etc) has no prerequisites
listed. (And often seen where make is used recursively - generally
something to avoid![2]) The consequencies of this mistake are an
incomplete dependency graph, and incorrect build results, as you
observe.

[1] .PHONY ensures it will be re-made every time, but this isn't
relevant to your problem.
[2]
http://members.canb.auug.org.au/~mi...-cons-harm.html

>=20
> JJ


João Jerónimo

2006-12-18, 7:07 pm

toby wrote:
>
> This is an unreliable workaround. (One reason is because the directory
> timestamp does not reflect all changes to files inside it - it will
> update when files are added or removed, but you can change contents of
> a file without this being reflected in the parent dir's timestamp.)


On Linux it worked well...
But I know the importance of portability!

>
> NOT if it exists[1]. The build command is being skipped because make
> sees that 'bootsect.MBR' already exists and is satisfied. Because the
> rule has no pre-requisites, it has no way to know if it is out of date.


It's strange that I said a so-stupid-thing, because a while before I had
concluded that it's only executed if the file doesn't exist...
The first "version" of the bootldr's Makefile was a "dummy" one... it
only created a file with many zeros using dd:

---------------------------------
all: bootldr.sys

bootldr.sys:
dd if=/dev/zero of=bootldr.sys count=somecount bs=somevalue
cp bootldr.sys ..
---------------------------------

So the file would only be created if it had been deleted...

> The correct fix to your problem is to add the correct prerequisite(s)
> to your rule for bootsect.MBR - which are the input files used by the
> rule's command(s). The same needs to be done for 'bootldr.sys' rule.


The problem of this is that it breaks structure... The idea is that this
Makefile should not need to know anything about how the file is
generated... it only needs to invoke make inside bootsect/ and see if it
was updated...

> [2]
> http://members.canb.auug.org.au/~mi...-cons-harm.html


It's not very important, but I could figure out this document's date...
In the abstract it says 1997 but the other pages in the PDF say it's 29
March 2006!

JJ
toby

2006-12-18, 7:07 pm

Jo=E3o Jer=F3nimo wrote:
> toby wrote:
me:[color=darkred]
ng...[color=darkred]
r",[color=darkred]
of[color=darkred]
>
> On Linux it worked well...
> But I know the importance of portability!


It's not a portability issue; it's a correctness issue.

>
>
> It's strange that I said a so-stupid-thing, because a while before I had
> concluded that it's only executed if the file doesn't exist...


It is executed if the target does not exist, or if it is out of date.
Without specifying prerequisites, make cannot determine if it is out of
date, so simple existence will skip the commands.

> The first "version" of the bootldr's Makefile was a "dummy" one... it
> only created a file with many zeros using dd:
>
> ---------------------------------
> all: bootldr.sys
>
> bootldr.sys:
> dd if=3D/dev/zero of=3Dbootldr.sys count=3Dsomecount bs=3Dsomevalue
> cp bootldr.sys ..
> ---------------------------------
>
> So the file would only be created if it had been deleted...


This rule in fact perfectly correct, because it *has* no
pre-requisites; the file cannot be "out of date" except with respect to
commands in the Makefile itself (if you were being very correct, you'd
add a dependency on the Makefile).

>
>
> The problem of this is that it breaks structure... The idea is that this
> Makefile should not need to know anything about how the file is
> generated... it only needs to invoke make inside bootsect/ and see if it
> was updated...


This is a problem you have to solve. Currently, you're not giving make
enough information for it to work properly. It will probably be helpful
to avoid sub-makes, although a correct solution can still be recursive.

>
>
> It's not very important, but I could figure out this document's date...
> In the abstract it says 1997 but the other pages in the PDF say it's 29
> March 2006!
>=20
> JJ


João Jerónimo

2006-12-19, 7:10 pm

toby wrote:
>
> It's not a portability issue; it's a correctness issue.


It's portability too, because it depends on the way the kernel-mode VFS
subsystem manages directory timestamps...

>
> This is a problem you have to solve.


Right!

> Currently, you're not giving make
> enough information for it to work properly. It will probably be helpful
> to avoid sub-makes, although a correct solution can still be recursive.


I'll try to follow your suggestion and try to avoid the use of recursive
Make for now on...
Although, as the boot sector (indeed, bootsect/ contains a first stage
boot loader) and the boot loader proper are separate programs and no
*real* linking (concatenation is not the same as linkage!) is made
between them, I think I'll keep using recursive make for them...

However, I need to make Make always execute make -C bootsect... So I
need to declare it as .PHONY, right?!
Right!
[color=darkred]

I think I'll not write so-sophisticated solutions to avoid recursive
Makes... I'm not exactly an expert in Make nor in shell scripting, and I
don't like to write code I don't understand!
[color=darkred]

How old is that document?

JJ
toby

2006-12-19, 7:10 pm


Jo=E3o Jer=F3nimo wrote:
> toby wrote:
>
> It's portability too, because it depends on the way the kernel-mode VFS
> subsystem manages directory timestamps...


It certainly does not work on my Linux system. To rely on this kind of
thing would be an error in any case.

>
is[color=darkred]
it[color=darkred]
>
> Right!
>
>
> I'll try to follow your suggestion and try to avoid the use of recursive
> Make for now on...
> Although, as the boot sector (indeed, bootsect/ contains a first stage
> boot loader) and the boot loader proper are separate programs and no
> *real* linking (concatenation is not the same as linkage!) is made
> between them, I think I'll keep using recursive make for them...


I explained the issue earlier, but to recap. Let's talk about these two
rules:

bootsect.MBR:
make -C bootsect
bootldr.sys:
make -C bootldr

The problem is that you don't give the true pre-requisites of these
files. Therefore make's dependency graph is incomplete. Therefore it
can't reliably know whether the targets are out of date. Therefore your
build is unreliable.

As you say above, adding true pre-requisites does "leak" some details
from the inner Makefile into this one. This is a mild hint that perhaps
recursive make isn't called for here.

I have a simple way of explaining how Makefiles are written - basically
it is, start with the final product, and *work backwards*. For each
artefact, decide what it depends on, and write its build commands. It's
important, as you see here, that the pre-requisites are complete.

>
> However, I need to make Make always execute make -C bootsect... So I
> need to declare it as .PHONY, right?!


..PHONY is *not* the correct solution, because instead of repairing the
incomplete graph, you're saying "always rebuild", which defeats the
purpose of using make in the first place.

> Right!
>
>
> I think I'll not write so-sophisticated solutions to avoid recursive
> Makes... I'm not exactly an expert in Make nor in shell scripting, and I
> don't like to write code I don't understand!


I'm happy to help you work through it until you have a good Makefile
that we both understand :) Contact me privately if you prefer, or
jabber qu1j0t3 <at> gmail.com

>
>
> How old is that document?


Is it important? It remains relevant - the problem you're having, and
its causes, is covered in detail there.

>=20
> JJ


toby

2006-12-19, 7:10 pm

Jo=E3o Jer=F3nimo wrote:
> ...
> I'll try to follow your suggestion and try to avoid the use of recursive
> Make for now on...
> Although, as the boot sector (indeed, bootsect/ contains a first stage
> boot loader) and the boot loader proper are separate programs and no
> *real* linking (concatenation is not the same as linkage!) is made
> between them, I think I'll keep using recursive make for them...


This reasoning sounds strange. The -details- of how a file is built
don't matter to make; it might be linking, as in creating an
executable, or it might be concatenating, or anything. All make wants
to know are the dependencies. This is expressed by:

both: one two
cat one two > both

Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2008 codecomments.com