Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Why can't I use $(@D) in a Makefile prerequisite?

When I compile my C files and output the resulting object files into their own directory, I need to make sure this directory exists.
I have seen two approaches for this:

  1. Always create the directory of the current target:
    obj/%.o: src/%.c
        @mkdir -p $(@D)
        $(CC) ...
    
  2. Add the obj/ directory as a prerequisite
    obj/%.o: src/%.c | obj/
        $(CC) ...
    
    obj/:
        mkdir -p $@
    

From 1. I like that it uses $(@D) to avoid repetition and to make it more robust, from 2. I like that mkdir is called only once and I feel like it makes more sense conceptually.

When I try to combine both like this

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

obj/%.o: src/%.c | $(@D)
    $(CC) ...

obj/:
    mkdir -p $@

then the compiler will complain that the obj/ directory does not exist.

>Solution :

Because that’s how make works; from the manual:

It’s very important that you recognize the limited scope in which automatic variable values are available: they only have values within the recipe. In particular, you cannot use them anywhere within the target list of a rule; they have no value there and will expand to the empty string. Also, they cannot be accessed directly within the prerequisite list of a rule. A common mistake is attempting to use $@ within the prerequisites list; this will not work. However, there is a special feature of GNU make, secondary expansion (see Secondary Expansion), which will allow automatic variable values to be used in prerequisite lists.

If you’re asking, why does make work like this, it’s because make always expands targets and prerequisites when it’s parsing the makefile, not when it matches the rules. Otherwise, something like this could not work:

OBJ = foo

$(OBJ)/%.o : %.c | $(OBJ) ;

OBJ = bar

$(OBJ)/%.o : %.cpp | $(OBJ) ;

Trying to special case SOME variables or functions to expand immediately and some be deferred until later, would be a mess and even harder to explain and understand. And of course, until the rule actually matches we don’t know what $(@D) should be, because the % could contain a directory.

The link given in the manual excerpt above explains how to use Secondary Expansion to provide some form of this behavior.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading