Makefile with Multiple Targets

How to build multiple targets from one makefile

Parameterized variable names and target-specific variables may do what you want, as the value of a target-specific variable is normally "inherited" by the prereqs of that target (assuming you are using GNU make):

target1_SRC=123 456
target2_SRC=abc def

target1: TARGET=target1
target2: TARGET=target2

target1: all
target2: all

all: ; @echo $($(TARGET)_SRC)

Then you can run make target1 or make target2, for example:

$ make target1
123 456
$ make target2
abc def

How to specify multiple targets in makefile?

In the first place, make recognizes a default target, not a default rule. The default target is the first one appearing in the file that does not start with a . and a capital letter. If you run make without designating a target to build, then it assumes you want the default target built, and in your case, that is hellomain.s. (And only hellomain.s, even though that appears in a rule that designates more than one target.)

The behavior that make exhibits for you is thus utterly normal, notwithstanding any use of a grouped-target rule (see below). You run make without designating a target, so it interprets you to want to build the default target. That target already exists and is up to date. That another target described in the makefile is missing is irrelevant. If you want to rebuild it with your current makefile then you could tell make so explicitly:

make sayhello.s

Now a few words about grouped-target rules. Note well that support for these is

  1. specific to rather recent versions of GNU make, and not available on most make implementations, including, for example, the versions of GNU make included in many Linux distributions as of the time of this writing.

  2. not intended for the use to which you are putting it.

Grouped-target rules address a longstanding issue of make's design: how to describe targets that are unavoidably created together. The canonical example would probably be the outputs of yacc or bison: a C source file and corresponding header file, both generated together by one run of the same program. You probably haven't yet the experience with make to appreciate how hard it is to describe that properly to traditional makes, but it is simple in GNU make 4.3 and later.

You have created a similar situation artificially. Don't do that. If you want to build multiple targets via one rule, then the idiomatic way to do it is to give them their own rules, and to name them as prerequisites to the same rule. Moreover, it is conventional, albeit not obligatory, to name the default target "all" in such cases. Example:

src_prefix = ../../src/hello

all: hellomain.s sayhello.s

hellomain.s : $(src_prefix)/hellomain.c
gcc -S $(src_prefix)/hellomain.c -o hellomain.s

sayhello.s : $(src_prefix)/sayhello.c
gcc -S $(src_prefix)/sayhello.c -o sayhello.s

clean :
-rm -f hellomain.s sayhello.s

.PHONY : all clean

Not that that makefile really exhibits good form generally. I've kept it pretty close to your original for didactic purposes, but if I were writing it, and assuming GNU make, then I would probably go with something more like this:

src_prefix = ../../src/hello

# A variable naming the targets, so I don't need to repeat the list, or risk
# different copies getting out of sync:
ASSEMBLY_TARGETS = hellomain.s sayhello.s

# specify the assembler command as a variable, near the top, to enable it to be
# easily changed:
AS = gcc -S

# default target named "all", with all targets I want to build as its prerequisites
all: $(ASSEMBLY_TARGETS)

# One pattern rule covering both targets. Pattern rules are GNU-specific, but
# I already stipulated that I'm writing for GNU make
%.s : $(src_prefix)/%.c
$(AS) $^ -o $@

# The clean rule also relies on the ASSEMBLY_TARGETS variable, so that we don't need
# to repeat the target list explicitly here.
clean :
-rm -f $(ASSEMBLY_TARGETS)

.PHONY : all clean

How to build specific multiple targets parallelly using make?


Unfortunately, it works sequentially

That is not true, as you could see from writing a small test:

$ cat Makefile
all: one two

one two:
@echo start $@
@sleep 2
@echo stop $@

$ make
start one
stop one
start two
stop two

$ make -j2
start one
start two
stop one
stop two

$ make -j2 one two
start one
start two
stop one
stop two

As you can see even when providing specific targets on the command line, they are run in parallel. If you are not seeing this behavior then there's something about your makefiles that is materially different than what you've described in your question.

GNU Make: Batching multiple targets in single command invocation

If you have GNU make 4.3 or above, you can use grouped targets like this:

DST_FILES = 1.dst 2.dst 3.dst
SRC_FILES = $(_DST_FILES:.dst=.src)

all: $(DST_FILES)

$(DST_FILES) &: $(SRC_FILES)
convert -o '{}.dst' $?
@touch $(DST_FILES)

If your convert is only updating some of the targets then you need the explicit touch to update the rest.

Here's a way to do it with passing a goal on the command line that might work; change DST_FILES to:

DST_FILES := $(or $(filter %.dst,$(MAKECMDGOALS)),1.dst 2.dst 3.dst)

Makefile rule with multiple targets for single invocation

You're using an old version of GNU make that doesn't understand &: rules, so just treats & as a file name. Use make --version to see which version you are running and upgrade if it is older that 4.3

How to make multiple targets by the same rule using target-dependent compilers?

See the documentation for target-specific variables: it's quite clear that target-specific variables take effect only in recipes. You cannot use them in prerequisites, or of course when defining targets.

It's probably simpler to do this without target-specific variables and just use pattern rules instead:

TEST = test_gcc test_clang
.PHONY: all

all: $(TEST)

test_%: hello_%
./$<

hello_%: hello.c
$* $< -o $@

(I don't know why you're removing the binary immediately after you test it, in this version, so I removed that).

GNU make: several targets in one pattern rule

This is exactly how this works; it's a long-standing feature. From the documentation:

Pattern rules may have more than one target; however, every target must contain a % character. Pattern rules are always treated as grouped targets (see Multiple Targets in a Rule) regardless of whether they use the : or &: separator.

As example states, it was meant to deal with programs that generate more than one output in one invocation, like bison. You can either update your recipe to generate both files in one shot, or keep the rules separated as you do now.

Makefile : Multiple targets in dedicated folders

As you have the gnu-make tag let's use GNU make:

targets  := target1 target2
BIN := mylib.so
DIR_SRC := ../sources/src
DIR_INC := ../sources/inc
SRC := $(wildcard $(DIR_SRC)/*.c)
OBJ := $(patsubst $(DIR_SRC)/%.c,%.o,$(SRC))
CFLAGS := -fPIC -I$(DIR_INC)
LDFLAGS := -shared -Wl,--start-group -Wl,--no-undefined
LDLIBS := -lm

.PHONY: all $(targets) clean
all: $(addsuffix /$(BIN),$(targets))

clean:
rm -rf $(addsuffix /objects,$(targets)) $(addsuffix /$(BIN),$(targets))

$(targets): %: %/$(BIN)

%.o:
$(CC) $(CFLAGS) -c $< -o $@

$(addsuffix /objects,$(targets)):
\mkdir -p $@

# $1: target
define target_rule
$1-LSRC := $$(wildcard $1/addsrc/*.c)
$1-LOBJ := $$(patsubst $1/addsrc/%.c,$1/objects/%.o,$$($1-LSRC))
$1-GOBJ := $$(addprefix $1/objects/,$$(OBJ))
$1-OBJ := $$($1-LOBJ) $$($1-GOBJ)

$$($(1)-LOBJ): $1/objects/%.o: $1/addsrc/%.c
$$($(1)-GOBJ): $1/objects/%.o: $$(DIR_SRC)/%.c

$$($1-OBJ): CFLAGS += -I./$1/addsrc
$$($1-OBJ): | $1/objects

$1/$$(BIN): $$($1-OBJ)
$$(CC) $$(CFLAGS) $$(LDFLAGS) -o $$@ $$^ $$(LDLIBS)
endef
$(foreach t,$(targets),$(eval $(call target_rule,$t)))

This makes use of GNU make specific features, especially the foreach-eval-call. It also uses order-only prerequisites for the directories, and the the standard make variables (LDFLAGS, CC, LDLIBS...) instead of the ones you were using.

Dependencies in the header files are not expressed because you do not explain what they should be. Adapt.

Demo:

$ tree
.
├── build
│   ├── makefile
│   ├── target1
│   │   └── addsrc
│   │   └── c.c
│   └── target2
└── sources
├── inc
│   ├── a.h
│   └── b.h
└── src
├── a.c
└── b.c

7 directories, 6 files
$ cd build
$ make
\mkdir -p target1/objects
\mkdir -p target2/objects
cc -fPIC -I../sources/inc -I./target1/addsrc -c target1/addsrc/c.c -o target1/objects/c.o
cc -fPIC -I../sources/inc -I./target1/addsrc -c ../sources/src/a.c -o target1/objects/a.o
cc -fPIC -I../sources/inc -I./target1/addsrc -c ../sources/src/b.c -o target1/objects/b.o
cc -fPIC -I../sources/inc -I./target2/addsrc -c ../sources/src/a.c -o target2/objects/a.o
cc -fPIC -I../sources/inc -I./target2/addsrc -c ../sources/src/b.c -o target2/objects/b.o
cc -fPIC -I../sources/inc -shared -o target1/mylib.so target1/objects/c.o target1/objects/a.o target1/objects/b.o -lm
cc -fPIC -I../sources/inc -shared -o target2/mylib.so target2/objects/a.o target2/objects/b.o -lm
$ make clean
rm -rf target1/objects target2/objects target1/mylib.so target2/mylib.so
$ make target1
\mkdir -p target1/objects
cc -fPIC -I../sources/inc -I./target1/addsrc -c target1/addsrc/c.c -o target1/objects/c.o
cc -fPIC -I../sources/inc -I./target1/addsrc -c ../sources/src/a.c -o target1/objects/a.o
cc -fPIC -I../sources/inc -I./target1/addsrc -c ../sources/src/b.c -o target1/objects/b.o
cc -fPIC -I../sources/inc -shared -o target1/mylib.so target1/objects/c.o target1/objects/a.o target1/objects/b.o -lm
$ make target2
\mkdir -p target2/objects
cc -fPIC -I../sources/inc -I./target2/addsrc -c ../sources/src/a.c -o target2/objects/a.o
cc -fPIC -I../sources/inc -I./target2/addsrc -c ../sources/src/b.c -o target2/objects/b.o
cc -fPIC -I../sources/inc -shared -o target2/mylib.so target2/objects/a.o target2/objects/b.o -lm

GNU Makefile Multiple rules in multiple targets

As I read the question, you have programs or scripts ej1_gen and ej2_jen in the project, serving to generate the wanted assembly sources. They each take the name of the output file as a command-line argument. Parts of this answer would need to be adjusted if that's a misinterpretation.


Rules to describe how to build the assembly files should designate the resulting assembly file(s) as the target. Also, supposing that the code-generator programs are part of the project, they should be designated as prerequisites, since changing those could cause them to produce different outputs. Any configuration files or similar that they read to inform their results should also be named as prerequisites (not shown). That leads to rules something like this:

ex1.asm: ej1_gen
./ej1_gen $@

ex2.asm: ej2_gen
./ej2_gen $@

It sounds like you may be asking for a way to express that via just one rule covering both, but I would not do so in this case. I don't think you get any clearer than the above, even if there are more than two assembly files to generate. It might be different if the same code generator program were being used, with different options, to generate all the assembly files, or perhaps if the generator name could be derived more directly from the target name.

With those rules in place, you can write a generic suffix rule or pattern rule to assemble the resulting files. Since you tag [gnu], I'll assume that a pattern rule is acceptable:

%.o: %.asm
nasm -g -o $@ -f elf32 $<

And you can take a similar approach to expressing a link rule:

%: %.o alfalib.o
$(CC) $(FLAGS) -m32 -o $@ $^

With that, you should be able to get rid of the ej variable and the exs target, too, leaving

all: $(ex)

as the only other rule (and it should still appear first in the file, as it does now).



Related Topics



Leave a reply



Submit