Home > Enterprise >  How to write a common rule in Makefile for multiple target dependency pairs
How to write a common rule in Makefile for multiple target dependency pairs

Time:01-25

I want to write a single Makefile to compile 3 different C files with their own independent targets. All these three files reside in the same directory along with other unused files as well. For example, the directory may contain files a.cpp, b.cpp, c.cpp and d.cpp. Out of these four, I want the Makefile to only run the build command for the first three files i.e. a.cpp, b.cpp and c.cpp. I'm storing these three files in a variable in the Makefile. Similarly, I'm also storing their targets in another variable. I want make to use a.cpp to compile the target a, b.cpp to compile the target b, and c.cpp to compile the target c. This is, however, just an example. The real project has hundereds of targets. Therefore, I do not want to write the rules for each of the targets manually. Is there a way in which I can tell make to compile each target using it's specific dependency?

To test out different ways to do this, I created a small test project with four files: the Makefile, aa, bb, and cc. Using the Makefile, I want to generate the targets a, b and c. All these three targets have only one dependency which is their source file. For example, a has dependency on aa. I wrote a test Makefile to do this which is pasted below:

TARGETS = a b c                                                                                                                                                
DEPS = aa bb cc

$(TARGETS): $(DEPS)
        @echo $@ $<

This Makefile simply prints out the targets and their dependencies. When I execute this Makefile, I get:

a aa

However, what I want is the following:

a aa
b bb
c cc

How can I accomplish this? Note that I need to specifically build the objects which are stored in the variable TARGETS and nothing else.

CodePudding user response:

The reason it only builds one target is that make only builds the first target in the makefile by default. In your example after variables are expanded make sees these rules:

a b c: aa bb cc
        @echo $@ $<

This is identical (to make) as if you'd written this:

a: aa bb cc
        @echo $@ $<
b: aa bb cc
        @echo $@ $<
c: aa bb cc
        @echo $@ $<

Since a is the first target, when you type make it will just build a. If you want make to build ALL the targets, make your first target something that depends on them all, such as:

TARGETS = a b c                                                                                                                                                
DEPS = aa bb cc

all: $(TARGETS)

$(TARGETS): $(DEPS)
        @echo $@ $<

But of course you can see above that this isn't what you want because each target has ALL the prerequisites so when bb changes, all of a, b, and c are rebuilt.

To do this you need to use a pattern rule. For example if you want to build a from a.cpp you can write your makefile like this:

TARGETS = a b c                                                                                                                                                
DEPS = aa bb cc

all: $(TARGETS)

%: %.cpp
        @echo $@ $<

CodePudding user response:

If the target name is not derived from the dependency name, you could also do something like this:

TARGETS = a b foo

all:$(TARGETS)                                                                                                                                             

a:aa
b:bb
foo:bar

$(TARGETS):
        @echo $@ $<

.PHONY: all
.PHONY: $(TARGETS)

Note that I declared your TARGETS as PHONY as your recipe does not create files with those names.

  • Related