Makefile merge similar rules into generic one - makefile

I have the following pattern in Makefile:
%_a0_b0.o: %.c
$(CMD) a0 b0 -i $^ -o $#
%_a1_b0.o: %.c
$(CMD) a1 b0 -i $^ -o $#
%_a0_b1.o: %.c
$(CMD) a0 b1 -i $^ -o $#
%_a1_b1.o: %.c
$(CMD) a1 b1 -i $^ -o $#
As I will increase the number of possibles combinaisons ai bj I would like to be able to merge all thoses rules in one.
I was able to use the same command on each recipes thanks to some text manipulation functions and the reverse function described here: Simplest way to reverse the order of strings in a make variable.
reverse = $(if $(wordlist 2,2,$(1)),$(call reverse,$(wordlist 2,$(words $(1)),$(1))) $(firstword $(1)),$(1))
A=$(word 2, $(call reverse,$(subst _, , $(notdir $#))))
B=$(word 1, $(call reverse,$(subst _, , $(notdir $#))))
%_a0_b0.o: %.c
$(CMD) $(A) $(B) -i $^ -o $#
%_a1_b0.o: %.c
$(CMD) $(A) $(B) -i $^ -o $#
%_a0_b1.o: %.c
$(CMD) $(A) $(B) -i $^ -o $#
%_a1_b1.o: %.c
$(CMD) $(A) $(B) -i $^ -o $#
But I'm stuck with the target: prerequisite part, Is there a way to merge thoses four rules to something more generic ?

If you are using GNU make you can use a combination of foreach, eval and call to instantiate make rules in a loop nest:
A := a0 a1
B := b0 b1
define MY_rule
%_$(1)_$(2).o: %.c
$$(CMD) $(1) $(2) -i $$^ -o $$#
endef
$(foreach a,$(A),$(foreach b,$(B),$(eval $(call MY_rule,$(a),$(b)))))
Note: the $$ in the macro definition are not typos, they are needed because this macro is expanded twice. Have a look at the this part of the GNU make manual for the details.

Related

how to factorise makefile targets?

I have the following 'working' snippet:
c_file_path := toto.c
asm_file_path := toto.S
c_files = $(notdir $(c_file_path))
asm_files = $(notdir $(asm_file_path))
vpath %.c $(dir $(c_file_path))
vpath %.S $(dir $(asm_file_path))
OBJDIR=objs/
c_objs:=$(addprefix $(OBJDIR),$(patsubst %.c,%.o,$(c_files)))
asm_objs:=$(addprefix $(OBJDIR),$(patsubst %.S,%.S.o,$(asm_files)))
$(info c_files $(c_files))
$(info s_files $(asm_files))
$(info c_objs $(c_objs))
$(info asm_objs $(asm_objs))
%.c:
#touch $#
%.S:
#touch $#
objs/%.o:%.c
#mkdir -p $(OBJDIR)
#gcc -c $^ -o $#
objs/%.S.o:%.S
#mkdir -p $(OBJDIR)
#gcc -c $^ -o $#
clean:
#rm -rf objs/*
all: $(c_objs) $(asm_objs)
#ls $(c_objs) $(asm_objs)
#echo "hello"
I tried to merge targets:
c_file_path := toto.c
asm_file_path := toto.S
c_files = $(notdir $(c_file_path))
asm_files = $(notdir $(asm_file_path))
vpath %.c $(dir $(c_file_path))
vpath %.S $(dir $(asm_file_path))
OBJDIR=objs/
c_objs:=$(addprefix $(OBJDIR),$(patsubst %.c,%.o,$(c_files)))
asm_objs:=$(addprefix $(OBJDIR),$(patsubst %.S,%.S.o,$(asm_files)))
$(info c_files $(c_files))
$(info s_files $(asm_files))
$(info c_objs $(c_objs))
$(info asm_objs $(asm_objs))
%.c %.S:
#touch $#
objs/%.o objs/%.S.o:%.c %.S
#mkdir -p $(OBJDIR)
#gcc -c $^ -o $#
clean:
#rm -rf objs/*
all: $(c_objs) $(asm_objs)
#ls $(c_objs) $(asm_objs)
#echo "hello"
which gives:
gcc: error: toto.c no such file or directory
why weren't target merged?
how to merge targets?
I'm not sure what you're trying to do but this rule:
objs/%.o objs/%.S.o:%.c %.S
tells make that for two files foo.c and foo.S (for any string foo) they can be used to create the targets objs/foo.o and objs/foo.S.o by invoking the recipe (gcc) one time.
I'm pretty sure that that's not what you want to say because it means you can't use this pattern unless both files foo.c and foo.S exist for a given string foo.
If what you're asking is how to reduce these two pattern rules:
objs/%.o:%.c
#mkdir -p $(OBJDIR)
#gcc -c $^ -o $#
objs/%.S.o:%.S
#mkdir -p $(OBJDIR)
#gcc -c $^ -o $#
into a single pattern rule so you don't have to write it twice the answer is very simple:
You can't.
You could put the recipe into a variable, like this:
build = #mkdir -p $(OBJDIR) && gcc -c $^ -o $#
then you can simplify the pattern rules somewhat like this:
objs/%.o : %.c ; $(build)
objs/%.S.o : %.S ; $(build)
but that's the best you can do, without resorting to overly-complicated things like foreach/eval/call loops etc.

How to build apps in subfolders with Makefile

I'm trying to write a Makefile that will build apps stored in subfolders. So structure of the folders is as follows:
examples/
Makefile
hello_world/
file1.c
main.c
**hello_world** <- expected build result
example1/
otherfile.c
main.c
**example1** <- expected build result
example2/
main.c
...
**example2** <- expected build result
My Makefile (simplified) looks like this:
EXAMPLES := hello_world example1 example2
SRC = $(wildcard */*.c)
OBJ = ${SRC:.c=.o}
OUT = $(foreach e, $(EXAMPLES), $(addprefix $(e)/,$(e)) )
CC := gcc
all: examples
$(OBJ): $(SRC)
#$(CC) -c $< -o $#
$(OUT): $(OBJ)
$(CC) $(filter $(dir $#)%,$^) -o $# $(LDFLAGS)
clean:
#rm -f $(OUT) $(OBJ)
Is there a way to make it work? Or maybe I should have Makefile in each folder separately?
This rule seems to solve my problem, matches outputs to .o, but filters files that are passed for linking with filter function
$(OUT): $(OBJ)
$(CC) $(filter $(dir $#)%,$^) -o $# $(LDFLAGS)
Replaced old rule:
$(OUT): $(OBJ)
$(CC) $^ -o $#
with
examples: $(OBJ)
$(foreach e, $(EXAMPLES), \
$(CC) $(wildcard $(e)/*.o) -o $(e)/$(e) $(LDFLAGS); \
)
almost works now, after clean, the compilation works as expected, only linking fails ($(wildcard $(e)/*.o) is empty?), after second run examples are created as expected
Inspired by comments by Beta and MadScientist, I think I have a solution:
EXAMPLES := hello_world example1 example2
SRC = $(wildcard */*.c)
OBJ = ${SRC:.c=.o}
OUT = $(foreach e, $(EXAMPLES), $(addprefix $(e)/,$(e)) )
CC := gcc
all: $(OUT)
%.o: %.c
#$(CC) $(CFLAGS) -c $< -o $#
$(OUT): $(OBJ)
#$(CC) $(filter $(dir $#)%,$^) -o $# $(LDFLAGS)
clean:
#rm -f $(OUT) $(OBJ)
.PHONY: all clean

How to do a makefile function that generate rules an return to build objects

I have the current code
It allow me to easily generate object files of binary files
define ASSET_RULE
$(addsuffix .o,$(basename $(1))): $(1)
$(LD) -r -b binary $(1) -o $(addsuffix .o,$(basename $(1)))
endef
SRC=avatar.jpg \
avatar2.png
OBJ=$(addsuffix .o,$(basename $(SRC)))
NAME=assets.a
all: $(NAME)
$(foreach file,$(SRC), $(eval $(call ASSET_RULE,$(file))))
$(NAME): $(OBJ)
$(AR) rcs $(NAME) $(OBJ)
clean:
$(RM) $(OBJ)
fclean: clean
$(RM) $(NAME)
re: fclean all
.PHONY: clean fclean all re
I would like to do a function that take assets as parameter, generate rules for them and return me the .o file to compile as dependencies.
By the end I expect to have something like.
ASSETS=avatar.jpg \
avatar2.png
OBJ=$(call BINARY_ASSETS,$(ASSETS))
NAME=assets.a
all: $(NAME)
$(NAME): $(OBJ)
$(AR) rcs $(NAME) $(OBJ)
clean:
$(RM) $(OBJ)
fclean: clean
$(RM) $(NAME)
re: fclean all
.PHONY: clean fclean all re
I tried to combine my foreach loop of eval with the final object names however I'm getting *** prerequisites cannot be defined in recipes. when I'm doing that.
Is it possible to do this kind of things ?
You'll need to update OBJS inside ASSET_RULE, because the macro will only be able to return the makefile code for $(eval). I've left out all the parts from your makefile that aren't relevant for the problem at hand:
# $(1): source file name
define ASSET_RULE
_obj_name := $(addsuffix .o,$(basename $(1)))
$$(_obj_name): $(1)
$(LD) -r -b binary $$< -o $$#
OBJS += $$(_obj_name)
_obj_name :=
endef
# $(1): list of source files
BINARY_ASSETS = $(eval \
$(foreach _a,$(1), \
$(call ASSET_RULE,$(_a)) \
) \
)
ASSETS := \
avatar.jpg \
avatar2.png
OBJS :=
$(call BINARY_ASSETS,$(ASSETS))
.PHONY: all
all: $(OBJS)
Test run where I replaced the eval with info to show the generated code:
$ make
_obj_name := avatar.o
$(_obj_name): avatar.jpg
ld -r -b binary $< -o $#
OBJS += $(_obj_name)
_obj_name :=
_obj_name := avatar2.o
$(_obj_name): avatar2.png
ld -r -b binary $< -o $#
OBJS += $(_obj_name)
_obj_name :=
make: Nothing to be done for 'all'.
If you want to update different variables with BINARY_ASSETS then you'll need a second parameter to pass in the variable name:
# $(1): source file name
# $(2): variable name to add object file name to
define ASSET_RULE
_obj_name := $(addsuffix .o,$(basename $(1)))
$$(_obj_name): $(1)
$(LD) -r -b binary $$< -o $$#
$(2) += $$(_obj_name)
_obj_name :=
endef
# $(1): list of source files
# $(2): variable name to add object file names to
BINARY_ASSETS = $(eval \
$(foreach _a,$(1), \
$(call ASSET_RULE,$(_a),$(2)) \
) \
)
$(call BINARY_ASSETS,$(ASSETS),OBJS)
OBJ=$(call BINARY_ASSETS,$(ASSETS))
This is wrong, as OBJ's value, i.e. $(call ...), gets expanded (i.e. invoked) later, inside of other recipes and prerequisites, which is the cause of make's complain. You may use only simple assignment here:
OBJ:=$(call BINARY_ASSETS,$(ASSETS))
Consult make's manual on two flavors of variables (if you find, like me, this text poorly understandable, just try to imagine that '=' operator in make assigns to a variable a literal string untouched).
Also, I strongly suggest to make use of automatic variables whenever possible, e.g.:
$(NAME): $(OBJ)
$(AR) $(ARFLAGS) $# $?

Makefile to compile lists of source files

I have lists of files that I want my Makefile to compile, one list for each source language:
CFILES= Src/Application/main.c Src/Core/data.c Lib/routines.c
ASFILES= Src/Application/startup.s Lib/sqrt.s
I want all the output in one directory:
OBJDIR= output
How do I do the equivalent of:
output/main.o : Src/Application/main.c
cc -c -o output/main.o Src/Application/main.c
output/data.o : Src/Core/data.c
cc -c -o output/data.o Src/Core/data.c
output/routines.o : Lib/routines.c
cc -c -o output/routines.o Lib/routines.c
output/startup.o : Src/Application/startup.s
as -o output/startup.o Src/Application/startup.s
output/sqrt.o : Lib/sqrt.s
as -o output/sqrt.o Lib/sqrt.s
The recipes are the same for every file in its list.
The input files are in all sorts of different directories and it is not acceptable to just list their filenames and use a search path to find them, their explicit paths must be used.
The output filename is the basename of the source file name with the extension changed to o. There are no duplicated basenames between the lists for the different source languages.
I do not want to have to list the object files, this should be derived from the source lists.
I am using gnu make, but bonus points for a portable solution.
Something like the following could do:
all :
OBJDIR := output
CFILES := Src/Application/main.c Src/Core/data.c Lib/routines.c
ASFILES := Src/Application/startup.s Lib/sqrt.s
target = ${OBJDIR}/$(patsubst %.s,%.o,$(notdir ${1}))
obj.c :=
obj.s :=
define obj
$(call target,${1}) : ${1} | ${OBJDIR}
obj$(suffix ${1}) += $(call target,${1})
${1} : ; mkdir -p `dirname $$#` && touch $$# # Create the source for testing. Remove this.
endef
define SOURCES
$(foreach src,${1},$(eval $(call obj,${src})))
endef
$(eval $(call SOURCES,${CFILES}))
$(eval $(call SOURCES,${ASFILES}))
all : ${obj.c} ${obj.s}
${obj.c} : % :
#echo cc -c -o $# $^; touch $# # echo and touch are for testing. Remove these.
${obj.s} : % :
#echo as -o $# $^; touch $# # echo and touch are for testing. Remove these.
${OBJDIR} :
mkdir $#
.PHONY: all
Output:
$ make
make: Entering directory '/home/max/tmp'
mkdir -p `dirname Src/Application/main.c` && touch Src/Application/main.c # Create the source for testing. Remove this.
mkdir output
cc -c -o output/main.c Src/Application/main.c
mkdir -p `dirname Src/Core/data.c` && touch Src/Core/data.c # Create the source for testing. Remove this.
cc -c -o output/data.c Src/Core/data.c
mkdir -p `dirname Lib/routines.c` && touch Lib/routines.c # Create the source for testing. Remove this.
cc -c -o output/routines.c Lib/routines.c
mkdir -p `dirname Src/Application/startup.s` && touch Src/Application/startup.s # Create the source for testing. Remove this.
as -o output/startup.o Src/Application/startup.s
mkdir -p `dirname Lib/sqrt.s` && touch Lib/sqrt.s # Create the source for testing. Remove this.
as -o output/sqrt.o Lib/sqrt.s
make: Leaving directory '/home/max/tmp'
I have little experience in writing makefiles, so this is just an attempt. In my example I have C and C++ files in a few directories and build a program made of these files.
$ cat Makefile
.PHONY : clean all
CC=gcc
CXX=g++
CFILES = c/f.c c/g.c
CPPFILES = cpp/main.cpp
OUTPUT = ./output
SOURCE_DIRS := $(dir $(CFILES))
SOURCE_DIRS += $(dir $(CPPFILES))
VPATH = $(sort $(SOURCE_DIRS))
C_FILENAMES := $(notdir $(CFILES))
CPP_FILENAMES += $(notdir $(CPPFILES))
OBJ_FILES := $(patsubst %.c, $(OUTPUT)/%.o, $(C_FILENAMES) )
OBJ_FILES += $(patsubst %.cpp, $(OUTPUT)/%.o, $(CPP_FILENAMES) )
all : $(OUTPUT)/program
$(OUTPUT)/program : $(OBJ_FILES)
g++ -o $# $^
$(OUTPUT)/%.o : %.cpp
$(shell mkdir -p $(OUTPUT) )
$(CXX) $(CXXFLAGS) -c $< -o $#
$(OUTPUT)/%.o : %.c
$(shell mkdir -p $(OUTPUT) )
$(CC) $(CFLAGS) -c $< -o $#
clean:
rm -fr $(OUTPUT)
And this is an example of using my makefile:
$ make
gcc -c c/f.c -o output/f.o
gcc -c c/g.c -o output/g.o
g++ -c cpp/main.cpp -o output/main.o
g++ -o output/program output/f.o output/g.o output/main.o
The following method should do what you want: compile all of your specified source files and put the object files in the directory ./output automatically. Of course, you need to provide compiler options, proper libraries necessary for linking, and so on.
OBJDIR =./output
SRCDIR1 =./Src/Application
SRCDIR2 =./Src/Core
SRCDIR3 =./Lib
SRC1 =$(SRCDIR1)/main.c
SRC2 =$(SRCDIR2)/data.c
SRC3 =$(SRCDIR3)/routines.c
SRC4 =$(SRCDIR1)/startup.s
SRC5 =$(SRCDIR3)/sqrt.s
OBJ1 =$(patsubst $(SRCDIR1)/%.c,$(OBJDIR)/%.o,$(SRC1))
OBJ2 =$(patsubst $(SRCDIR2)/%.c,$(OBJDIR)/%.o,$(SRC2))
OBJ3 =$(patsubst $(SRCDIR3)/%.c,$(OBJDIR)/%.o,$(SRC3))
OBJ4 =$(patsubst $(SRCDIR1)/%.s,$(OBJDIR)/%.o,$(SRC4))
OBJ5 =$(patsubst $(SRCDIR3)/%.s,$(OBJDIR)/%.o,$(SRC5))
vpath %.c $(SRCDIR1): $(SRCDIR2): $(SRCDIR3)
vpath %.s $(SRCDIR1): $(SRCDIR3)
all: $(OBJ1) $(OBJ2) $(OBJ3) $(OBJ4) $(OBJ5)
cc $^ -o executable
$(OBJDIR)/%.o: %.c | $(OBJDIR)
cc -c $< -o $#
$(OBJDIR)/%.o: %.s | $(OBJDIR)
cc -c $< -o $#
$(OBJDIR):
mkdir -p $(OBJDIR)

How to produce different objects with the same file

I am trying to modify a Makefile, and I can't find the rule for that.
The following doesn't work. I don't know how to write the src to obj rules.
# foo.c bar.c main.c
SRC = $(wildcard *.c)
OBJ_1 = $(patsubst %.c,%_1.o,$(SRC))
OBJ_2 = $(patsubst %.c,%_2.o,$(SRC))
GCC1 = vtcc
GCC2 = vtcc
LD_FLAGS= -lm -lpthread
all: a1 a2
# executables :
a1: $(OBJ_1)
$(GCC1) $(LDFLAGS) $^ -o $#
a2: $(OBJ_2)
$(GCC2) $(LDFLAGS) $^ -o $#
# objects :
$(OBJ_1) : $(SRC)
$(GCC1) -c $< -o $#
$(OBJ_2) : $(SRC)
$(GCC2) -c $< -o $#
It's hard tell what your question is, but I think the last two rules should be:
# objects :
$(OBJ_1) : %_1.o : %.c
$(GCC1) -c $< -o $#
$(OBJ_2) : %_2.o : %.c
$(GCC2) -c $< -o $#

Resources