make: *** No rule to make target 'dir/bar', needed by 'foobar'. Stop.

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

make: *** No rule to make target 'dir/bar', needed by 'foobar'. Stop.

Brian J. Murrell
I wonder if somebody can explain to me why this Makefile:

foo:
        touch foo
        touch bar

%/:
        mkdir -p $@

dir/%: % | dir/
        rm -f $@
        ln $< $@

foobar: dir/foo dir/bar


doesn't work as one might think it should:

$ rm -rf dir foo bar; make foobar
touch foo
touch bar
mkdir -p dir/
rm -f dir/foo
ln foo dir/foo
make: *** No rule to make target 'dir/bar', needed by 'foobar'.  Stop.

Everything looks good for dir/bar to be created:

$ ls -l foo bar dir/
-rw-rw-r--. 1 brian brian    0 Jan 28 12:53 bar
-rw-rw-r--. 2 brian brian    0 Jan 28 12:53 foo

dir/:
total 0
-rw-rw-r--. 2 brian brian 0 Jan 28 12:53 foo

Subsequently trying to create dir/bar even works:

$ make dir/bar
rm -f dir/bar
ln bar dir/bar

Any ideas?  What am I missing?

Cheers,
b.


signature.asc (499 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

RE: make: *** No rule to make target 'dir/bar', needed by 'foobar'. Stop.

Cook, Malcolm-2

This does not directly answer your question...

My general advice is to not try to find ways to have directories as targets or as dependencies of rules.

One pattern that I find addresses many use cases is to unconditionally shell out to create all directories that will be used in the makefile, as:

$(shell mkdir -p dir)

> -----Original Message-----
> From: Help-make <help-make-bounces+mec=[hidden email]> On
> Behalf Of Brian J. Murrell
> Sent: Thursday, January 28, 2021 12:10
> To: [hidden email]
> Subject: make: *** No rule to make target 'dir/bar', needed by 'foobar'.
> Stop.
>
> I wonder if somebody can explain to me why this Makefile:
>
> foo:
> touch foo
> touch bar
>
> %/:
> mkdir -p $@
>
> dir/%: % | dir/
> rm -f $@
> ln $< $@
>
> foobar: dir/foo dir/bar
>
>
> doesn't work as one might think it should:
>
> $ rm -rf dir foo bar; make foobar
> touch foo
> touch bar
> mkdir -p dir/
> rm -f dir/foo
> ln foo dir/foo
> make: *** No rule to make target 'dir/bar', needed by 'foobar'.  Stop.
>
> Everything looks good for dir/bar to be created:
>
> $ ls -l foo bar dir/
> -rw-rw-r--. 1 brian brian    0 Jan 28 12:53 bar
> -rw-rw-r--. 2 brian brian    0 Jan 28 12:53 foo
>
> dir/:
> total 0
> -rw-rw-r--. 2 brian brian 0 Jan 28 12:53 foo
>
> Subsequently trying to create dir/bar even works:
>
> $ make dir/bar
> rm -f dir/bar
> ln bar dir/bar
>
> Any ideas?  What am I missing?
>
> Cheers,
> b.

Reply | Threaded
Open this post in threaded view
|

Re: make: *** No rule to make target 'dir/bar', needed by 'foobar'. Stop.

Paul Smith-20
In reply to this post by Brian J. Murrell
On Thu, 2021-01-28 at 13:09 -0500, Brian J. Murrell wrote:

> foo:
>         touch foo
>         touch bar
>
> %/:
>         mkdir -p $@
>
> dir/%: % | dir/
>         rm -f $@
>         ln $< $@
>
> foobar: dir/foo dir/bar
>
>
> doesn't work as one might think it should:
>
> $ rm -rf dir foo bar; make foobar
> touch foo
> touch bar
> mkdir -p dir/
> rm -f dir/foo
> ln foo dir/foo
> make: *** No rule to make target 'dir/bar', needed by 'foobar'.
> Stop.

It's because your makefile doesn't define a rule to build "bar", so
make doesn't know how to build it.

When make starts, "bar" doesn't exist.  None of the rules make invokes
say that they build "bar".  So make doesn't know it exists.  This is
due to GNU make's directory caching facility, which is a performance
improvement to avoid reading the contents of directories more than one
time when possible.

That's why it works the second time: the second time when make starts,
the "bar" target already exists so make sees it.

The next release of GNU make will implement a workaround that should
avoid this issue.

In the meantime you'll have to explain to make that a single invocation
of this target builds both outputs.  An easy way to fix it is to just
make two rules:

  foo: ; touch foo
  bar: ; touch bar

Assuming there's a reason you can't do that, then you can do something
like this:

  foo bar: .sentinel ; @:
  .sentinel:
          touch foo
          touch bar