markvgti
markvgti

Reputation: 4619

GNU make: How to force an intermediate dependency to be made when it doesn't exist

There are three types of files. For convenience lets call them type1 (*.1 files), type2 (*.2 files) and type3 (*.3 files). Type2 is made from type1 by running a certain command; type3 is made from type2 by running a certain other command.

Type2 files age and become invalid, therefore are deleted from time to time.

I have rules like:

%.2 : %.1
    cmd1 $< -o $@

%.3 : %.2
    cmd2 $< > $@

Now if xyz.1 and xyz.3 exist (xyz.1 is not newer than xyz.3), and xyz.2 has been deleted, running make xyz.3 simply says that everything is up-to-date, and nothing needs to be done.

If I touch xyz.1 then run make, everything works as expected.

I need make xyz.3 to first create the type2 file, then re-create the type3 file. Is there any way to do this?

Versions:

Update 1: Made it clearer that pattern rules are being used (thanks for pointing that out, @Louis), and that more than one file of each type exists.

Upvotes: 1

Views: 657

Answers (2)

Louis
Louis

Reputation: 151521

The Problem

The problem is due to the use of patterns:

all: foo.3

%.2: %.1
    touch $@

%.3: %.2
    touch $@

In this case make foo.3; rm foo.2; make foo.3 will not result in foo.2 and foo.3 being remade. In fact, with the Makefile above make will by itself delete foo.2 after it creates foo.3 because foo.2 is an intermediate target and that's what make does with intermediate targets. (See here.)

The Solution

The solution is simple, list foo.2 as an explicit target:

all: foo.3

foo.2: %.2: %.1
    touch $@

%.3: %.2
    touch $@

By having foo.2 listed explicitly, it is no longer considered intermediate. The syntax used above is documented here. Presumably there are more than one file to which the rule applies so a more general solution would be:

# Get all files that end in `.1` from the filesystem.
type1_files:=$(wildcard *.1)

# For each file that ends in `.1` we want a corresponding file that ends in `.3`.
type3_files:=$(type1_files:%.1=%.3)

all: $(type3_files)

# For each file that ends in `.1` we want a corresponding file that ends in `.2`.
type2_files:=$(type1_files:%.1=%.2)

# All files that end in `.2` are explicit targets to which this rule applies.
$(type2_files): %.2: %.1
    touch $@

%.3: %.2
    touch $@

Upvotes: 2

Brave Sir Robin
Brave Sir Robin

Reputation: 1046

The following works as you expect:

type2 : type1
    @echo building $@ from $<
    touch $@

type3 : type2
    @echo building $@ from $<
    touch $@

Result:

$ touch type1
$ make type3
building type2 from type1
touch type2
building type3 from type2
touch type3
$ rm type2
$ make type3
building type2 from type1
touch type2
building type3 from type2
touch type3

Make should not treat type2 as intermediate unless you tell it to, so your example doesn't exhibit the problem. Try to make a test case that does.

Upvotes: 0

Related Questions