Search code examples
makefilegnu

Second Expansion: Evaluate pattern rule before GNU function call


There is a rule in my makefile for each object file. The corresponding source file and a hidden dependency file are the prerequisites.

Example: obj/test/bar.o : src/test/bar.c src/test/.bar.d

Due to a potentially large number of object files, I'd like to generalize this rule. My first attempt was the following:

$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)  $(dir $(SRCDIR)/%.$(DEPSEXT)).$(notdir $(SRCDIR)/%.$(DEPSEXT))

with

$(BUILDDIR):=obj
$(SRCDIR):=src
$(OBJEXT):=o
$(SRCEXT):=c
$(DEPXEXT):=d

The problem is that the pattern rule inside the second prerequisite evaluates after dir and notdir have been applied. As a consequence, the second prerequisite for the above example would be /src/.test/bar.d instead of /src/test/.bar.d.

As an alternative, I tried to generate the correct prerequisites with the following shell commands:

%.$(OBJEXT): $(shell echo %.$(SRCEXT) | sed -e 's/^[^\/]*/src/') $(shell echo %.$(SRCEXT) |  sed -e 's/^[^\/]*/src/;s/^\(.*\/\)/\1./')

I am using sed to perform string substitution on the object file. The regular expressions are tested and correct. Although the wildecard %is visible to echo, I cannot pipe it into sed. Therefore, sed receives an empty string and outputs a "wrong" result.

Questions:

  1. How do I have to adjust the rule such that it works properly?
  2. Why does echo see %, but why is it impossible to pipe it into the sed?

Solution

  • Using a shell function doesn't work for exactly the same same reason that using dir etc. doesn't work. They are both make functions, and they're both expanded the same way, so why would one of them work when the other doesn't?

    The problem you have is that rule introduction lines are expanded by make when the makefile is parsed. But pattern rules are not applied until much later, when make tries to actually build rules. When make parses the makefile it has absolutely no idea what the % will expand to: that expansion is not done until the rule is applied.

    So in all your cases, make functions (which includes shell) are operating on the literal character %.

    Usually, dependency files are put into different directories rather than manipulating the filename, such as .deps/foo.d, to avoid these types of problems.

    However, you can do what you want with secondary expansion as follows:

    .SECONDEXPANSION:
    
    $(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT) $$(dir $(SRCDIR)/$$*.$(DEPSEXT)).$$(notdir $(SRCDIR)/$$*.$(DEPSEXT))