Make question concerning "$$@"

Martin Weitzel martin at mwtech.UUCP
Sun May 5 09:44:47 AEST 1991


In article <1499 at cacilj.UUCP> jurgen at cacilj.UUCP (Jurgen Heymann) writes:
>It seems that string replacement doesn't work for "$$@"!
>
>I am trying to write make files for the following situation:
>
>  - source files are in one directory 
>  - object files and targets are in a SUBdirectory of the sources
>    (one subdir for each different machine we compile for).
>    THe MAKEFILE is in this object directory.
>
>In a makefile I want to say that the object files depend on the source
>files in the parent dir. I write:
>
>$(SRCFILES:.c=.o) : ../$($@:.o=.sim)
>
>but that gives funny values for $$@ (try make -d) and "$$(@:.o=.sim)"
>doesn't work (syntax error from make).
>When I just write "$$@", I of course get the object file for $$@.

I stumbled over this too some time ago. I didn't have the time then
to investigate this further, but this article made me curious again,
and now I seem to have found an explanation together with a workaround.

The source of all problems seems to be that macros in dependency lines
may be expanded twice. (I got alarmed for that one day when read what
someone wrote in a text about make, that "$$@" is `dynamically' expanded.)

In the first phase after reading the whole Makefile, MAKE does macro
expansion on dependency lines. During working from the leaves to the root
of the dependency tree later, MAKE will do macro expansion a second time,
on dependency lines as well as on command lines.

Normally you will not notice much difference for the two possible occasions
where macros in dependency lines get expanded, but you can verify what I
wrote by trying the following Makefile:

	mac = foo

	bar:	$(mac)
		@echo "$?"

If file `foo' is newer than `bar', the echo will be excuted, otherwise MAKE
tells you that `bar' is up to date. Now change the dependency line:

	mac = foo
	#       vv---------- note THIS!
	bar:	$$(mac)
		@echo "$?"

The result is the same. BINGO! Not only the $$ gets expanded to $, later
`$(mac)' is recognized and gets expanded to `foo'. Now let's change the
example again:

	mac = foo foo2

	bar:	$(mac)
		@echo "$?"

This will work as expected (provided some file `foo2' exists and is newer
than `bar', it will be included in the message printed by echo; if `foo2'
doesn't exist MAKE complains that it doesn't know how to make `foo2').

	mac = foo foo2
	#	vv----------- again: "late expansion"
	bar:	$$(mac)
		@echo "$?"

In this example MAKE will complain that it doesn't know how to make
`foo foo2', i.e. it looks for a file with a blank in its name. This
clearly shows that the two passes of macro expansion are in fact done
differently!

So we have found the truth why we must write `$$@' if we want the
"current target" in dependency lines, but name the same thing as `$@'
in command lines: We simply must suppress the expansion during the first
pass! `$$' gets expanded to `$' and what is stored in the dependency tree
is `$@', the same thing that is used in command lines (for which obviously
only one pass of macro expansion is done!)

Now let's turn back to the original question:

	$(SRCFILES:.c=.o) : ../$($@:.o=.sim)

With the assumption that SRCFILES is initialized with `foo.c' and `bar.c',
further experiments show that the above expands to:

	foo.o	:	../
	bar.o	:	../

(If the files foo.c and bar.c exist, MAKE further appends them to the
dependencies because of the suffix rule ".c.o:". This doesn't matter
too much here, I just write it so that you don't get confused if you try
to experiment with some simple Makefiles to verify what I've written.)
How comes this expansion: Because MAKE expands the contents of a macro
with the name `$@'.

Be careful with what I wrote here. Normally if you speak about macros
you need not to try to avoid confusion between the name of a macro and
the demand for expansion of this macro. Let's take a simple example:
If `SRCFILES' is the name of a macro, `$(SRCFILES)' is the demand to
expand this macro. Now, what do I demand from MAKE if I write $($@) ?
The expansion of a macro with name `$@' which is quite different from
the builtin macro `@', for which I demand expansion with `$@' !!
Now add suffix replacement to the above and you'll see the light ...
at least I saw it when I was this far :-)

Back to the problem.

(The next line contains a formfeed and makes your news reader propably
stop at this point. One reason for this formfeed is to to make you more
curious for the solution which will follow soon. Another, more trivial
reason will be obvious as soon as you display the next page.)

What do we want *after* the first pass of macro expansion? Obviously:

	foo.o	:	../$(@:.o=.sim)         <--+
	bar.o	:	../$(@:.o=.sim)         <--+-----------------------+
                                                                           |
This would lead us directly to:                                            |
                                                                           |
	$(SRCFILES:.c=.o) : ../$$(@:.o=.sim)                               |
                                                                           |
as our specification in the Makefile. But as the original poster noticed,  |
this leaves us with an syntax error. (As I don't have the sources of MAKE  |
I'm not sure what makes MAKE stumble, but I strongly assume that it is the |
colon as you can have dependency lines that read `a : b c (x)' but non     |
that read `a : b c:x', i.e. it is not possible to embedd a colon in a      |
components name on a dependency line.                                      |
                                                                           |
As I promised above, the intention of this article is not only to bore     |
you with details about how and why things don't work as expected - I also  |
have a workaround. Again have a look at the line we want to have *after*   |
the first expansion step. -------------------------------------------------+
			 (If you guess now that this arrow is the reason for
			  the formfeed above, you're absolutely right :-))
	
How can we write this in the original Makefile, if we cannot write a
colon in a dependency line?

The trick that works (at least on ISC UNIX) is to hide the colon in
a macro definition. I tried several solutions and came finally up with:

	o_sim = $(@:.o=.sim)

	$(SRCFILES:.c=.o) : ../$$(o_sim)

During building the dependency tree, this (only) expands to:

	foo.o	: ../$(o_sim)
	bar.o	: ../$(o_sim)

Later, when dependencies are checked, macros are further expanded with
the final result of:

	foo.o	: ../foo.sim
	bar.o	: ../bar.sim

Also the following works as desired:

	simfiles = ../$(@:.o=.sim)

	$(SRCFILES:.c=.o) : $$(simfiles)

But in any case be sure to double the `$' in the dependency line.
Otherwise macro substitution will allready recurse during the first phase,
since MAKE detects that expansion of one macro yields in another macro.
The important difference in the above workaround is that MAKE obviously
does *not* detect that the concatenation of some expanded macro ($$ -> $)
with a fixed string ((o_sim)) results into a construct that again looks
like an expandable macro. (In aother words: MAKE only looks for the
expansion buffer not for the context, if it has to decide about recursive
macro expansion.)

As the earlier experiments show, we can be quite lucky with this behaviour
since MAKE recognizes and expands the macro later during checking the
dependencies. This expansion ($(o_sim) -> $(@:.c=.sim)) results in what we
need, and as this time the new text fully appears in the expansion buffer,
MAKE recurses with macro expansion and gives us either `foo.sim' or
`bar.sim', depending on the current target. 

OK for now, it's a rather long article again, but I think by showing the
way how to come to the solution, the readership has gained a little more
insight and understanding for some peculiarities of MAKE. (For me too it
was quite instructive figuring all this out.)
-- 
Martin Weitzel, email: martin at mwtech.UUCP, voice: 49-(0)6151-6 56 83



More information about the Comp.unix.wizards mailing list