OrangeDog
OrangeDog

Reputation: 38777

Define a choice of prerequisites in a pattern rule

For example, lets say I have a compiler that can build foo files from either bar or baz sources.

The rules for this might look like:

%.foo: %.bar
    # commands to
    # invoke compiler

%.foo: %.baz
    # commands to
    # invoke compiler

However, this could start getting a bit long and redundant as the number of input types and recipe commands increase. Is there any syntax available to compress this into a single rule?

%.foo: $(oneof %.bar %.baz)
    # commands to
    # invoke compiler

Upvotes: 3

Views: 335

Answers (2)

edupo
edupo

Reputation: 99

What you propose at the beginning is right: Makefiles should be clear and concise regarding building rules.

In the other hand you may take a look at Canned Recipes to try to avoid repeating the same recipes once and again:

define MAKE_FOO =
#You may use automatic variables such as $^ or $@.
mv $< $@    #In this example just a file renaming.
endef

%.foo: %.bar
    $(MAKE_FOO)

%.foo: %.baz
    $(MAKE_FOO)

The canned recipe MAKE_FOO will expand to whatever recipes you write inside the define statement as if they were copied manually.

Upvotes: 2

Mike Kinghan
Mike Kinghan

Reputation: 61432

Here's an illustration for the concrete problem of making an .o file from either a .c file or a .cpp file with a combined pattern rule. An executable is also built to aid the illustration.

Makefile

.PHONY: all clean

all: test

%.o: %.c %.cpp
    gcc -c $?

test: main.o hw.o
    g++ -o $@ $^

clean:
    rm -f test *.o  

where we have:

hw.c

#include <stdio.h>

void hw(void)
{
    puts("Hello from C");
}

hw.cpp

#include <iostream>

extern "C" void hw()
{
    std::cout << "Hello from C++" << std::endl;
}

and:

main.cpp

extern "C" void hw(void);

int main(void)
{
    hw();
    return 0;
}

Make from clean and run:

$ make clean && make && ./test
rm -f test *.o
g++    -c -o main.o main.cpp
gcc -c hw.c hw.cpp
g++ -o test main.o hw.o
Hello from C++

Both hw.c and hw.cpp were compiled per the pattern rule.

Each one of them was compiled to the same object file, hw.o, with the second, C++ compilation overwriting the C compilation. So the C++ object file was linked, simply because it was the last to be built. Be clear about what you expect to happen when the combined rule is triggered by multiple prerequisites.

Now let's update hw.c and repeat:

$ touch hw.c
$ make && ./test
gcc -c hw.c
g++ -o test main.o hw.o
Hello from C

This time, hw.o was compiled only from hw.c, and linked.

Update hw.cpp and repeat:

$ touch hw.cpp
make && ./test
gcc -c hw.cpp
g++ -o test main.o hw.o
Hello from C++

Once again, the hw.o from C++ was linked.

The key element of the combined pattern rule is $?, which means all the prerequisites that are newer than the target

Upvotes: 1

Related Questions