'Makefile : Multiple targets in dedicated folders
I'm struggling with the make language and haven't been yet able to reach my goal. I have the following folder structure.
/sources/src => *.c
/sources/inc => *.h
/build => makefile
/build/target1/addsrc => *.c & *.h
/build/target2/addsrc => *.c & *.h
I want to compile both target using the command make or any target separately using one of the commands make target1 or make target2. Each target uses the same sources located in the sources folder, adds additional sources located its own addsrc folder and applies different compilation flags. The makefile should create the following directories and files:
/build/target1/objects => *.o
/build/target1 => mylib.so
/build/target2/objects => *.o
/build/target2 => mylib.so
I tried with the following makefile:
targets=target1 target2
TARGET=$(dir $@)
DIR_OBJ = $(TARGET)objects
DIR_BIN = $(TARGET)
BIN = mylib.so
DIR_SRC = ../sources/src
DIR_ADDSRC_LOC = ./$(TARGET)addsrc
INCLUDES = -I./$(TARGET)addsrc -I../sources/inc
SRC := $(notdir $(wildcard $(DIR_SRC)/*.c))
ifneq ($(DIR_ADDSRC_LOC),)
SRCLOC := $(notdir $(wildcard $(DIR_ADDSRC_LOC)/*.c))
endif
SRCS = $(SRC) $(SRCLOC)
OBJ = $(SRCS:%.c=$(DIR_OBJ)/%.o)
CFLAGS = -fPIC
LFLAGS = -shared -lm -Wl,--start-group -Wl,--no-undefined
all : $(targets)
$(targets): % : %/init %/$(BIN)
$(targets:%=%/init):
@if [ ! -d $(DIR_OBJ) ]; then \mkdir -p $(DIR_OBJ); fi;
@if [ ! -d $(DIR_BIN) ]; then \mkdir -p $(DIR_BIN); fi;
$(targets:%=%/$(BIN)): $(OBJ)
gcc $(OBJ) $(LFLAGS) -o $@
$(DIR_OBJ)/%.o: %.c
gcc $(INCLUDES) $(CFLAGS) -c $< -o $@
This works for the directory initialisation for each target. However, the rest is absolutely not working. I spent hours to have it working but without any success. Any help would be appreciated. Thanks !
Note: I haven't worked yet on the target dependency regarding the compilation flags.
Solution 1:[1]
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
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
